Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inline function and type extension

Tags:

inline

f#

Consider I have two different library types:

type Foo = { foo : string }
type Bar = { bar : int32 }

I want to implement generic function zoo that will work for either Foo or Bar instances. And I cannot change Foo and Bar because they are part of library code.

Here's my first attempt using type extensions and inline function as explained here:

// Library.fs
module Library

type Foo = { foo : string }
type Bar = { bar : int32 }

// Program.fs
type Foo with
    static member zoo (f : Foo) = "foo"

type Bar with
    static member zoo (b : Bar) = "bar"

let inline zoo (x : ^t) =
    (^t : (static member zoo : ^t -> string) x)

let f = zoo { foo = "1" } // error FS0001: The type 'Foo' does not support the operator 'zoo'

Why don't inline function definition relies on type extensions? How could I solve my problem without changing of the initial Foo and Bar type definitions?

like image 985
ForNeVeR Avatar asked Oct 02 '15 18:10

ForNeVeR


2 Answers

Use method overload.

The problem with extension methods is that they are not taken into account when solving member constraints .

So you can use method overload, as shown already in your own answer or you can go further and create an inline generic function by using an intermediate type and an intermediate method (in this case an operator for simplicity) to do the trick:

type T = T with
    static member ($) (T, x:Foo) = "foo"
    static member ($) (T, x:Bar) = "bar"

let inline zoo x = T $ x

let f = zoo { foo = "1" }

Here you have more details about how this works.

Be aware that this function will be inlined, so for instance you won't be able to call it from C#, if this is required don't use a function, use simple and standard method overload.

like image 57
Gus Avatar answered Nov 07 '22 09:11

Gus


The best thing I could get so far is

type Ext =
    static member zoo (f : Foo) = "foo"
    static member zoo (b : Bar) = "bar"

let f = Ext.zoo { foo = "1" } // "foo"
let b = Ext.zoo { bar = 2 } // "bar"

It is not the best and not very generic solution but at least it works.

like image 29
ForNeVeR Avatar answered Nov 07 '22 10:11

ForNeVeR