I found that:
abs -10
abs -10L
both work. So I wondered how F# implemented this and did a search in the source code:
type AbsDynamicImplTable<'T>() =
let AbsDynamic x = AbsDynamicImplTable<_>.Result x
[<CompiledName("Abs")>]
let inline abs (x: ^T) : ^T =
AbsDynamic x
when ^T : ^T = absImpl x
And I am confused with these.
As I know in a function like abs, we must compare the input with 0, and there are different 0s for different types.
Thanks.
To add some explanation to the code posted by ChaosPandion, the problem with F# functions like
absis that they can work with any numeric type. There is no way to express this just using F#/.NET generics – the only supported constrains are that type parameter implements a certain interface or has a constructor, but there is no constraint for numeric types.So, F# also supports static constraints (the type parameter is
^ainstead of usual'a) and these are processed at compile time using inlining (this also explains why the function has to beinline). You can write you own function with static constraints by using built-in functions fromLanguagePrimitiveswhich contains many useful functions that require some constraints:Note that constraints are inferred –
DivideByIntrequires that the type hasDivideByIntmember, so our function requires the same thing (and it will work with your own type if it has this member too, which is quite useful!).In addition to this, the implementation of
absuses two additional tricks that are allowed only in the F# library – it specifies different code (to be used when inlining) for different types (usingwhen ^a:int = ....) and a fallback case, which usesAbsmember, so it will work with any explicitly listed type or a type withAbsmember. Another trick is theretypefunction, which “changes the type”, but doesn’t contain any code – the only purpose is to make the code type-check, but this could be very unsafe – so this is used only in F# libraries.