2

我想将我的 F# OOP 版本的 Tagless Final 转换为典型的 FP 方法,并且我正在考虑使用来自 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 

问题是我收到警告

构造导致代码比类型注释所指示的更通用

对于两者cacheImpl1dataSourceImpl1所以我不能再requestData用于其他情况。有没有办法绕过这个问题?

4

2 回答 2

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)
于 2019-04-02T22:07:22.960 回答
0

我熟悉 final tagless,但我不确定你为什么要使用 SRTP。最终无标签使用类型类,这些可以用接口模拟(参见 scala 模拟类型类的方式)。

该方法类似于(基本相同)“对象代数”,可以使用标准 OO 构造来实现。

于 2020-02-22T09:35:15.577 回答