2017-10-17 50 views
3

我有以下代码如何变换OO调用到一些通用的函数调用

type Show<'a> = 
    abstract member Show: 'a -> string 

type Shows() = 
    member inline this.GetShow(x:string) = 
     {new Show<string> with member this.Show(x:string) = x} 
    member inline this.GetShow(x:int) = 
     {new Show<int> with member this.Show(x:int) = sprintf "%A" x} 

它完美地工作,如果我把它用正常OO符号。

printfn "100 %s" (Shows().GetShow("some").Show("some")) 

不过,我想换行成一个功能,以便

let inline show x = (Shows().GetShow(x).Show(x)) 

但是这给了我下面的错误

[FS0041] A unique overload for method 'GetShow' could not be determined based 
on type information prior to this program point. A type annotation may be 
needed. Candidates: 
member Shows.GetShow : x:int -> Show<int>, 
member Shows.GetShow : x:string -> Show<string> 

任何想法如何克服呢?

回答

3

这是否让你足够接近你想要的?

let inline GetShow p x = (^x : (member GetShow : ^p -> ^o) (x, p)) 
let inline Show p x = (^x : (member Show : ^p -> ^o) (x, p)) 

let inline show x s = s |> GetShow x |> Show x 

Shows() |> show "a" 
Shows() |> show 1 

如果您创建Shows外联函数这不是太难。这种方法不需要内联。

2

您必须使用静态解析的类型参数,并明确声明您希望该类型拥有具有所需签名的GetShow成员。此外,这只适用于静态成员。

type Shows() = 
    static member inline GetShow(x:string) = 
     {new Show<string> with member this.Show(x:string) = x} 
    static member inline GetShow(x:int) = 
     {new Show<int> with member this.Show(x:int) = sprintf "%A" x} 

let inline ($) (a: ^a) (b: ^b) = 
    ((^a or ^b): (static member GetShow : ^b -> Show< ^b>) b) 

let inline show x = (Shows() $ x).Show(x) 

结束语约束在一个单独的操作$是必要的,因为你只能指定静态解析的类型参数的约束 - 即你不能这样说(when Show : (member ...)),在不能使用的具体类型Show那里,必须是一个参数。所以我们引入一个中间函数$然后用Show作为参数调用它。

而我使用运算符$而不是常规函数的原因是静态解析的约束可以为运算符推断。使用常规函数,你必须写两次when ...子句 - 一次在签名中,一次在主体中。

相关问题