can someone explain to me how this piece of code works (I know what it does)?
let inline getLengthAndWidth s =
let v1=(^a: (member Length: _) s)
let v2=(^a: (member Width: _) s)
(v1, v2)
can someone explain to me how this piece of code works (I know what it does)?
let inline getLengthAndWidth s =
let v1=(^a: (member Length: _) s)
let v2=(^a: (member Width: _) s)
(v1, v2)
Stating the obvious getLengthAndWidth is a inline function that takes a parameter s
. But if we look closer we see that s
has type ^a
. That’s interesting because a generic type is represented with an apostrophe like 'a
. This is because it is an SRTP or Statically Resolved Type Parameter which operates differently than a generic type parameter, by the way, is only used in inline functions. This expression (^a: (member Length: _) s)
is harder to break down, however this shape of expression is only used for SRTP so it is not as widely known. It is a member access expression. For a record or object type a member access is done with the dot notation like (r : Rectangle).Length
. For a interface it is done in similar way and for a generic type it can’t be done because nothing can be assured about a generic type. But say if you could assure something about a generic type say at compile time … introducing SRTP. Back to this expression: (^a: (member Length: _) s)
what this tells the compiler is that I am accessing the member Length
of object s
which is of type ^a
. The compiler has to do the heavy lifting and assert than any object passed into the getLengthAndWidth
function must have a member Length
with a wildcard type _
which I like to call the “I don’t care” symbol. The how is more complicated than I know, however the trick is the in inline; and the compiler will produce the correct inline il for the actual types of s
used.
So if I have type A
with members Length and Width and both have are of type int
then the output type of the function for that specific inline instance of the function will be int * int
, meanwhile if I have a similar type B
except with the Length and Width types being float
the output signature of the function will be float * float
. With this expression:
let (a : A) = ...
let (b : B) = ...
let u,v = getLengthAndWidth a
let w,x = getLengthAndWidth b
The bindings u and v are going to be of type int and the bindings w and x are going to be of type float.
Notice that there are two function calls or “inline instances” each being called with an object of an explicit type.
I hope that helps.
It is a cool feature of F# which closes the gap with C++ templates which are statically resolved as well.