Реализация конечного кодирования без тегов в F# с SRTP
Я хотел бы преобразовать мою версию F# OOP Tagless Final в типичный подход FP, и я думаю использовать статически разрешенные параметры типа классов классов из ОО.
Что я сделал
open System
open FSharpPlus
type UserName = string
type DataResult<'t> = DataResult of 't with
static member Map ( x:DataResult<'t> , f) =
match x with
| DataResult t -> DataResult (f t)
создание SRTP мне нужно
type Cache =
static member inline getOfCache cacheImpl data =
( ^T : (member getFromCache : 't -> DataResult<'t> option) (cacheImpl, data))
static member inline storeOfCache cacheImpl data =
( ^T : (member storeToCache : 't -> unit) (cacheImpl, data))
type DataSource() =
static member inline getOfSource dataSourceImpl data =
( ^T : (member getFromSource : 't -> DataResult<'t>) (dataSourceImpl, data))
static member inline storeOfSource dataSourceImpl data =
( ^T : (member storeToSource : 't -> unit) (dataSourceImpl, data))
и их конкретные реализации
type CacheNotInCache() =
member this.getFromCache _ = None
member this.storeCache _ = ()
type CacheInCache() =
member this.getFromCache user = monad {
return! DataResult user |> Some}
member this.storeCache _ = ()
type DataSourceNotInCache() =
member this.getFromSource user = monad {
return! DataResult user }
type DataSourceInCache() =
member this.getFromSource _ =
raise (NotImplementedException())
с помощью которого я могу определить окончательный DSL без тегов
let requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
и такой вид работает следующим образом
[<EntryPoint>]
let main argv =
let cacheImpl1 = CacheInCache()
let dataSourceImpl1 = DataSourceInCache()
let cacheImpl2 = CacheNotInCache()
let dataSourceImpl2 = DataSourceNotInCache()
requestData cacheImpl1 dataSourceImpl1 "john" |> printfn "%A"
//requestData (cacheImpl2 ) dataSourceImpl2 "john" |> printfn "%A"
0
Проблема в том, что я получаю предупреждение
конструкция приводит к тому, что код будет менее общим, чем указано в аннотациях типов
для обоих cacheImpl1
а также dataSourceImpl1
и поэтому я не могу использовать повторно requestData
для другого случая. Есть ли способ обойти эту проблему?
1 ответ
Я не знаком с абстракцией, которую вы пытаетесь реализовать, но, глядя на ваш код, кажется, что вам не хватает inline
модификатор здесь:
let inline requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
В качестве примечания вы можете упростить функцию карты следующим образом:
type DataResult<'t> = DataResult of 't with
static member Map (DataResult t, f) = DataResult (f t)
Я знаком с final tagless, но я не уверен, зачем вам использовать SRTP. Final tagless использует классы типов, и их можно эмулировать с помощью интерфейсов (см. Способ, которым scala эмулирует классы типов).
Подход похож (в основном тот же) на "объектную алгебру", который может быть реализован с использованием стандартных объектно-ориентированных конструкций.