使用SRTP在F#中实现无标签最终编码 [英] Implementing Tagless Final Encoding in F# with SRTP

查看:86
本文介绍了使用SRTP在F#中实现无标签最终编码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将Tagless Final的F# OOP版本转换为典型的FP方法,我正在考虑使用"rel ="nofollow noreferrer">来自OO的类型类.

I'd like to transform my F# OOP version of Tagless Final into a typical FP approach and I'm thinking to use Statically Resolved Type Parameters of 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

creating the SRTP I need

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

by which I can define a tagless final 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导致代码的通用性低于类型指示的通用性注释

construct causes code to be less generic than indicated by the type annotations

同时用于 cacheImpl1 dataSourceImpl1 ,因此在其他情况下,我无法重用 requestData .有没有办法绕开这个问题?

for both cacheImpl1 and dataSourceImpl1 and so I can't reuse requestData for the other case. Is there a way to detour this issue?

推荐答案

我对您要实现的抽象并不熟悉,但是从您的代码来看,您似乎缺少了一个 inline 修饰符在这里:

I'm not familiar with the abstraction you're trying to implement, but looking at your code it seems you're missing an inline modifier here:

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) }

请注意,您可以像这样简化地图功能:

As a side note, you can simplify your map function like this:

type DataResult<'t> = DataResult of 't with
    static member Map (DataResult t, f) = DataResult (f t)

这篇关于使用SRTP在F#中实现无标签最终编码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆