Record interface

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.

2 Likes

It is a cool feature of F# which closes the gap with C++ templates which are statically resolved as well.