我想将Tagless Final的F#OOP version转换为典型的FP方法,并且我想使用Statically Resolved Type Parameters中的Type Classes from OO。
我所做的是
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
问题是我收到警告
construct导致代码的通用性低于类型指示的通用性 注释
同时用于cacheImpl1
和dataSourceImpl1
,因此在其他情况下我无法重用requestData
。
有没有办法绕开这个问题?
答案 0 :(得分: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)
答案 1 :(得分:0)
我熟悉最终的无标签,但是我不确定为什么要使用SRTP。 最终的无标记使用类型类,并且可以使用接口对其进行仿真(请参见scala仿真类型类的方式)。
该方法与“对象代数”相似(基本上相同),可以使用标准的OO构造来实现。