To add some explanation to the code sent by ChaosPandion, the problem with F # functions such as abs is that they can work with any numeric type. It is impossible to express this only using generic F # /. NET files - the only supported restrictions are that this type parameter implements a specific interface or has a constructor, but there are no restrictions for numeric types.
Thus, F # also supports static constraints (a parameter of type ^a instead of the usual 'a ), and they are processed at compile time using inlining (this also explains why the function should be inline ). You can write your own function with static constraints using the built-in functions from LanguagePrimitives , which contains many useful functions that require certain restrictions:
> let inline half (num: ^a) : ^a = LanguagePrimitives.DivideByInt< (^a) > num 2 ;; val inline half : ^a -> ^a when ^a : (static member DivideByInt : ^a * int -> ^a) > half 42.0;; val it : float = 21.0 > half 42.0f;; val it : float32 = 21.0f
Note that restrictions are inferred - DivideByInt requires that the type has a DivideByInt member, so our function requires the same thing (and it will work with your own type if it also has this member, which is very useful!).
In addition to this, the abs implementation uses two additional tricks that are only allowed in the F # library - it specifies different code (which will be used when embedding) for different types (using when ^a:int = .... ) and the backup case, which uses the abs member, so it will work with any explicitly specified type or type with the abs member. Another trick is the retype function, which "changes the type" but does not contain any code. The only goal is to do a code type check, but it can be very dangerous, so this is only used in the F # library.
Tomas Petricek Feb 03 '10 at 15:38 2010-02-03 15:38
source share