使用 SRTP 在 F# 中实现无标记最终编码
Implementing Tagless Final Encoding in F# with SRTP
我想改造我的 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
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 causes code to be less generic than indicated by the type
annotations
用于 cacheImpl1
和 dataSourceImpl1
,所以我不能在另一种情况下重复使用 requestData
。
有没有办法绕过这个问题?
我不熟悉您要实现的抽象,但查看您的代码,您似乎缺少 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 使用 type 类,这些可以用接口模拟(参见 scala emulates type类 的方式)。
该方法类似于(基本相同)"object algebra",可以使用标准 OO 构造来实现。
我想改造我的 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
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 causes code to be less generic than indicated by the type annotations
用于 cacheImpl1
和 dataSourceImpl1
,所以我不能在另一种情况下重复使用 requestData
。
有没有办法绕过这个问题?
我不熟悉您要实现的抽象,但查看您的代码,您似乎缺少 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 使用 type 类,这些可以用接口模拟(参见 scala emulates type类 的方式)。
该方法类似于(基本相同)"object algebra",可以使用标准 OO 构造来实现。