Generic type para­me­ters were intro­duced in C# 2.0, and they gave us the abil­ity to write code that works against any type that matches a set of con­straints and remove the need to cre­ate type-specific over­loads, e.g.:

image

A few years passed, and dynamic types was intro­duced in C# 4, which allows us to bypass compile-time type check­ing (type check­ing still hap­pens at run­time) when we use the dynamic key­word, e.g.:

image

this code works so long as the ‘+’ oper­a­tor is defined for the run­time types of obj1 and obj2, oth­er­wise, you’ll get a Run­time­BinderEx­cep­tion when this code is exe­cuted. This kind of type check­ing is often referred to as Duck typ­ing.

Duck typ­ing is a very pow­er­ful lan­guage fea­ture but it also makes your code less safe, as you now face the pos­si­bil­ity of hav­ing run­time type errors that would have oth­er­wise been caught by the compiler.

It’s a damn shame that there’s no way for you to com­bine the pow­ers of gener­ics and dynamic typ­ing.. because if you could, you’d be able to write DoSome­thin­gElse like this instead:

pub­lic void DoSomethingElse<T, U, V>(T obj1, U obj2) where T + U => V

{

    var obj3 = obj1 + obj2;

    Console.WriteLine(obj3);

}

which stops you from mis­tak­enly try­ing to invoke the method with an int and a List<int> at com­pile time, and saves you 10 mins of debug­ging and bug fix­ing time.

Well, would it blow your mind if I tell you that such fea­ture already exists in the .Net frame­work today? No, I kid you not, mem­ber con­straints in F# lets you do just that!

Mem­ber Constraints

In F#, you can spec­ify a sta­t­i­cally resolved type para­me­ter by pre­fix­ing it with the caret sym­bol ( ^ ), e.g. ^T. These type para­me­ters can be used with mem­ber con­straints which allows you to spec­ify that a type argu­ment must have a par­tic­u­lar mem­ber or mem­bers in order to be used.

Take the fol­low­ing F# code for instance:

image

It’s equiv­a­lent to the ear­lier C# ver­sion of DoSome­thin­gElse, but this func­tion has the fol­low­ing signature:

image

which says that the types ^a and ^b used in the func­tion must have a sta­tic mem­ber ‘+’ defined on either one of them that can be used on instances of these two types.

You can explic­itly state mem­ber con­straints too, for instance:

image

Mem­ber con­straints are very use­ful in those tight spots where you find your­self wrestling with the .Net type sys­tem, it gives you the flex­i­bil­ity of duck typ­ing but also the safety of sta­tic typ­ing, so really it gives you the best of both worlds!

Restric­tions

How­ever, use­ful as it may be, there are some restric­tions to when you can use mem­ber constraints.

First, they can­not be used with generic type para­me­ters, they can only be used with sta­t­i­cally resolved type parameters.

Sec­ondly, they can only be used with func­tions or meth­ods that are inline.

Inline func­tions

Which brings us to the topic of inline func­tions, which as the name sug­gests, cre­ates the func­tions ‘inline’.

But what does that mean? It means that, at every call site to an inlined func­tion or method, the con­crete types for the sta­t­i­cally resolved type para­me­ters (e.g. ^T, ^U) are resolved and the spe­cific code for those types are inserted at the call site.

This has the obvi­ous impli­ca­tion that the com­piler will gen­er­ate many dupli­cated IL code for inline func­tions and it increases the size of your assembly.

By con­trast, a nor­mal func­tion or method will gen­er­ate only one instance of that func­tion or method and every call site makes a ‘jump’ to the loca­tion of that func­tion or method in mem­ory to exe­cute it. The inlin­ing behav­iour also has some per­for­mance impli­ca­tions which we will go through shortly.

Restric­tions

The one major restric­tion on inline func­tions is that an inlined func­tion can­not be overloaded.

When to use inline functions

As with most pow­er­ful fea­tures, such as duck typ­ing and inline func­tions, they are often open to abuse.. Per­son­ally, I think it’s impor­tant for one to think care­fully about what it is that they’re try­ing to do before decid­ing to use the lat­est and great­est lan­guage fea­tures just for the sake of it.

Take the last code snip­pet for exam­ple, there’s no rea­son why I can’t solve this prob­lem using inter­faces and generic type para­me­ters – define an ISpeaker inter­face with a Speak mem­ber and requir­ing the para­me­ter x to be an instance of ISpeaker.

Doing so will achieve the same end goal, there will be a stronger con­tract (a well under­stood, shared inter­face) and the code will be cleaner and eas­ier to read:

image

As far as I’m aware, there are really only two main rea­sons for using inline functions:

Generic Arith­metic

This is the prob­lem inline func­tions were designed to solve, and a prob­lem that can’t be solved by using inter­faces and gener­ics because there’s no way to spe­cific con­straints on oper­a­tors with­out using mem­ber con­straints (and there­fore inline functions).

Take this sim­ple add func­tion for example:

image

with­out mak­ing it inline the para­me­ters a and b will be inferred to be of type int, and there’s no way to make this func­tion generic and usable with every numeric type (int32, int64, uint32, uint64, float, etc.). This is a prime exam­ple of when you should inline a func­tion, doing so solves the afore­men­tioned problem:

image

Per­for­mance Optimization

In some cases, it’s pos­si­ble to get a per­for­mance improve­ment by mak­ing a func­tion inline as it removes the jumps from call sites to the function’s loca­tion in memory.

Jon Har­rop gave this exam­ple of how by mak­ing the fol­low­ing fold func­tion inline it can run 5x faster:

image

image

As you can see, in my test, the inline ver­sion man­aged to run a whop­ping 8.5x faster!

This might just be too much temp­ta­tion to those of us con­stantly seek­ing bet­ter per­for­mance from our apps, but it’s impor­tant to keep a cou­ple of things in mind too:

  • mak­ing a func­tion or method inline does not always guar­an­tee a per­for­mance improvement
  • overuse can add sig­nif­i­cant bloat and makes your assem­bly big­ger and takes longer to load, hence poten­tially imped­ing over­all performance
  • always measure/profile your app first, don’t pre­ma­turely optimize

Fur­ther readings

MSDN – Sta­t­i­cally resolved type para­me­ters (pre­ceded by a caret ^ symbol)

MSDN – Inline functions

Sta­t­i­cally typed duck typ­ing in F#

Share

2 Responses to “F# – inline functions and member constraints”

  1. […] you should mark func­tions that requires generic com­par­i­son ‘inline’ to boost per­for­mance. Tweet Feb­ru­ary 19, 2012 | the­burn­ing­monk | Tags: .Net, F#, Func­tional Programming, […]

  2. Omu says:

    ^Awe­some (*!!!*)
    inline func­tions look like C++

Leave a Reply