Given the following code:
let DisplayImpl logger data =
data |> Seq.iter logger
printfn ""
let Working =
DisplayImpl (printfn "%O") [1;2;3]
DisplayImpl (printfn "%O") ["a";"b";"c"]
let NotWorking display =
display (printfn "%O") [1;2;3]
display (printfn "%O") ["a";"b";"c"]
~~~ ~~~ ~~~
The last line gives the error: This expression was expected to have type int but here has type string
I thought the following might work, but it doesn't:
let StillNotWorking (display: ('a -> unit) -> seq<'a> -> unit) =
My question is, how do I define the NotWorking function so that the display parameter stays generic within the function?
Functions that are passed as arguments to other functions (like your
display
) cannot be themselves polymorphic in F#. They can use the generic type parameter ('a
etc.), but the actual type for this parameter is specified when the main function (NotWorking
in your case) is called. This means that you can only calldisplay
with single actual type used for the type variable'a
in the body ofNotWorking
.As a workaround, you can use an interface with a generic method:
The method
Display
of the interface is itself generic method, so you can call that method multiple times with different type arguments (int
in the first case andstring
in the second).However, I didn't find this a limitation when writing normal code in F# very often, so maybe there is an easier solution for your problem (possibly, taking a non-generic
IEnumerable
or something simple like that - orobj list
as in the answer from John). If you give more details about your actual code, that would be useful.Some background, just in case you're interested in theoretical details, but none of this is really something that would be important in day-to-day real world F# programming. Anyway -
This is possible in other langauges like Haskell and the mechanism that allows it is called universal types. When you have a polymorphic function in F#, it essentially means that that the scope of the type variables is the entire function, so
('a -> unit) -> unit
can be viewed asforall 'a . ('a -> unit) -> unit
.When you call the function, you need to specify what
'a
is and that cannot be changed (i.e. you can't use the function'a -> unit
that you get as an argument with two different types for'a
once'a
is fixed).Using universal types, you can write
forall
yourself, so you can say that the type is:(forall 'a . 'a -> unit) -> unit
. Now the generic parameter'a
is only linked to the function that you'll get as an argument. The type of the function given as an argument is now itself a generic function and so you can call it with different types standing for'a
.PS: Value restriction is a different problem - that essentially means that F# cannot make things that are not syntactic functions generic, but in your example, you're writing syntactic functions, so that's not the issue here.