How does this annotation work, and why is there no other?

Please explain the magic behind the drawShape function. 1) Why does this work at all - I mean, what he calls the Draw member, 2) why should it be inline ?

 type Triangle() = member x.Draw() = printfn "Drawing triangle" type Rectangle() = member x.Draw() = printfn "Drawing rectangle" let inline drawShape (shape : ^a) = (^a : (member Draw : unit->unit) shape) let triangle = Triangle() let rect = Rectangle() drawShape triangle drawShape rect 

And the next problem - is it possible to write a drawShape function using parameter type annotation as shown below? I found that it has exactly the same signature as the first, but I cannot complete the body.

 let inline drawShape2 (shape : ^a when ^a : (member Draw : unit->unit)) = ... 

Thanks in advance.

+6
f #
May 18 '15 at 1:07
source share
1 answer

This syntax, similar to Voodoo, is called a " statically permitted type parameter ". The idea is to ask the compiler to verify that the type passed as a general argument has certain members on it (in your example, Draw ).

Since the CLR does not support such checks, they must be performed at compile time, which the F # compiler will gladly do for you, but it also has a price: because there is no CLR support, there is no way to compile such a function in IL, which means that it needs to be "duplicated" every time it is used with a new common argument (this method is also sometimes called " monomorphisation "), and for which the inline keyword is used.

As for the syntax of the call: for some reason, simply declaring a restriction on the parameter itself does not reduce it. You must declare it every time you really refer to a member:

 // Error: "x" is unknown let inline f (a: ^a when ^a: (member x: unit -> string)) = ax() // Compiles fine let inline fa = (^a: (member x: unit -> string)( a )) // Have to jump through the same hoop for every call let inline f (a: ^a) (b: ^a) = let x = (^a: (member x: unit -> string)( a )) let y = (^a: (member x: unit -> string)( b )) x+y // But can wrap it up if it becomes too messy let inline f (a: ^a) (b: ^a) = let callX t = (^a: (member x: unit -> string) t) (callX a) + (callX b) // This constraint also implicitly carries over to anybody calling your function: > let inline gxy = (fxy) + (fyx) val inline g : x: ^a -> y: ^a -> string when ^a : (member x : ^a -> string) // But only if those functions are also inline: > let gxy = (fxy) + (fyx) Script.fsx(49,14): error FS0332: Could not resolve the ambiguity inherent in the use of the operator 'x' at or near this program point. Consider using type annotations to resolve the ambiguity. 
+12
May 18 '15 at 13:53
source share



All Articles