robkuz
robkuz

Reputation: 9924

How to transform OO calls into a some generic function calls

I have the following code

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}

which works perfectly if I call it using normal OO notation.

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

However I'd like to wrap that into a function so that

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

But this gives me the following error

[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>

Any ideas how to overcome this?

Upvotes: 3

Views: 72

Answers (2)

TheQuickBrownFox
TheQuickBrownFox

Reputation: 10624

Does this get you close enough to what you want?

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

It's not too hard if you create your Shows outside of the inline function. This way the methods don't need to be inline.

Upvotes: 3

Fyodor Soikin
Fyodor Soikin

Reputation: 80744

You have to use statically resolved type parameters and explicitly state that you expect the type to have a GetShow member with the required signature. Also, this only works with static members.

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)

Wrapping up the constraint in a separate operator $ is necessary, because you can only specify statically resolved constraints on type parameters - i.e. you can't say something like (when Show : (member ...)), can't use the concrete type Show in there, has to be a parameter. So we introduce an intermediate function $ and then call it with Show as parameter.

And the reason I use an operator $ instead of a regular function is that statically resolved constraints get inferred for operators. With a regular function, you'd have to write the when ... clause twice - once in the signature, once in the body.

Upvotes: 2

Related Questions