Let's take a simple function value f1
:
let f1 = printfn "*bind f1*"; fun () -> printfn "f1()"
f1
binds in FSI as
*bind f1*
val f1 : (unit -> unit)
and, being invoked, behaves as expected
> () |> f1 |> f1;;
f1()
f1()
val it : unit = ()
Now let's take a similar function value, but made explicitly generic f2<'a>
:
let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"
f2
binds in FSI as
val f2<'a> : (unit -> unit)
without any *bind f2*
output whatsoever, but then, being invoked, outputs it on each f2
invocation:
> () |> f2 |> f2;;
*bind f2*
f2()
*bind f2*
f2()
val it : unit = ()
My question is: what may be the cause for such observed disparity?
F# does not generally allow creating of generic values because of the "value restriction" difficulties that this introduces (that is, you cannot create a syntactic value that is generic, even if it is a code that returns function). So, your f2
should not be allowed, but...
The rule has a single exception - it is often useful to have generic values like List.empty
and so if you declare a value with explicit generic type arguments, it actually is compiled into a function that returns the result.
That's exactly what happens in your example:
let f2<'a> = printfn "*bind f2*"; fun () -> printfn "f2()"
here, f2
is a generic value (even though it does not really use the type argument anywhere), but it has an explicit generic type argument and so it is actually compiled into a method that is called each time f2
is accessed and it returns the result (function unit -> unit
)
I could not find any clear explanation for this in the specification, but there is a good MSDN article (see "Case 4") and also a blog by former F# team member.
Tomas is right, as usual. Note that you can create a generic function that behaves as f1
does, but you've got to use a nominal generic type as a cache of sorts:
type private Container<'a> () =
static member val f2 : unit -> unit = printfn "*bind f2*"; fun () -> printfn "f2()"
let f2<'a>() = Container<'a>.f2()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With