Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type constraints for static methods

Tags:

f#

I am using OpenTK with its math library but unfortunately there is no generic interface for the vector classes. For example Vector2 ,3 and 4 all have the same static method SizeInBytes http://www.opentk.com/files/doc/struct_open_t_k_1_1_vector3.html#ae7cbee02af524095ee72226d842c6892

Now I could just overload tons of different constructors but I think it should be possible to solve this via type constraints.

I was reading though http://msdn.microsoft.com/en-us/library/dd233203.aspx and I found this

type Class4<'T when 'T : (static member staticMethod1 : unit -> 'T) > =
    class end

Now I have tried it by myself but I can't get the syntax right.

type Foo<'T when 'T: (static member SizeInBytes: unit -> int)>(data: 'T []) =
   member this.GetBytes() = 'T.SizeInBytes() 

let f = Foo([|new Vector3(1.0f,1.0f,1.0f)|])
f.GetBytes()

Can you spot the problem?

Edit: VS2012 complains about this line 'T.SizeInBytes() //Unexpected symbol or expression and T.SizeInBytes() doesn't work either.

Edit2:

I made an example that doesn't involve an external library

type Bar() = 
    static member Print() = printf "Hello Foo"

type Foo<'T when 'T: (static member Print: unit -> unit)>(data: 'T []) =
   member this.Print() = 'T.Print()

let b1 = Bar()
let f = Foo([|b1|])
f.Print()
like image 372
Maik Klein Avatar asked Dec 20 '22 19:12

Maik Klein


1 Answers

The correct syntax for calling things which are guaranteed by member constraints is a bit obscure:

type Foo< ^T when ^T: (static member SizeInBytes: unit -> int)>(data: ^T []) =
   member inline this.GetBytes() =
       (^T : (static member SizeInBytes : unit -> int) ())

Note that 'T has to be changed to a "statically resolved type variable" ^T - see the Glossary in the F# spec.

You can't call members specified by constraints on normal type variables, because that's not supported by the .NET framework, so F# has to compile them away. It's a syntax error if we try to use 'T in GetBytes instead.

I think the MSDN documentation is being a bit misleading by giving an example with 'T, because although you can write the type they give, you could never use the constraint.

If you look at the IL code for the Class4 sample, the constraint is actually gone:

.class nested public auto ansi serializable Class4`1<T>
    extends [mscorlib]System.Object

which makes sense because the member constraint has to be removed for .NET. The same is true for type Foo with the ^T type variable.

Note also that in common with all inline F# functions, you can only call it statically from F# code, so that the compiler can inline the definition at the call site.

It will throw an exception if you try to call it from C# code, or via reflection. If you try, your code will fail at runtime.

Generally working with F# constraints that aren't supported by .NET is a tricky business, so I'd steer clear if at all possible.

EDITED: I've substantially updated my original answer which incorrectly said this wasn't possible, in light of (a) my further experiments (b) Gene Belitski's answer and (c) idjarn's comment that inline functions always get compiled to IL that throws an exception.

like image 105
GS - Apologise to Monica Avatar answered Jan 03 '23 23:01

GS - Apologise to Monica