Please explain the magic behind drawShape
function. 1) Why it works at all -- I mean how it calls the Draw
member, 2) why it needs to 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 issue is -- is it possible to write drawShape
function using parameter type annotation like below? I found that it has exactly the same signature as the first one, but I'm unable to complete the body.
let inline drawShape2 (shape : ^a when ^a : (member Draw : unit->unit)) =
...
Thanks in advance.
This Voodoo-looking syntax is called "statically resolved type parameter". The idea is to ask the compiler to check that the type passed as generic argument has certain members on it (in your example -
Draw
).Since CLR does not support such checks, they have to be done at compile time, which the F# compiler is happy to do for you, but it also comes with a price: because there is no CLR support, there is no way to compile such function to IL, which means that it has to be "duplicated" every time it's used with a new generic argument (this technique is also sometimes known as "monomorphisation"), and that's what the
inline
keyword is for.As for the calling syntax: for some reason, just declaring the constraint on the parameter itself doesn't cut it. You need to declare it every time you actually reference the member: