Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I declare a generic parameter in F#?

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?

like image 701
Patrick McDonald Avatar asked Dec 21 '22 05:12

Patrick McDonald


2 Answers

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 call display with single actual type used for the type variable 'a in the body of NotWorking.

As a workaround, you can use an interface with a generic method:

type Displayer = 
  abstract Display : (obj -> unit) -> 'T list -> unit
 
let NotWorking (display:Displayer) = 
    display.Display (printfn "%O") [1;2;3] 
    display.Display (printfn "%O") ["a";"b";"c"] 

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 and string 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 - or obj 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 as forall '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.

like image 82
Tomas Petricek Avatar answered Jan 05 '23 19:01

Tomas Petricek


This works as well

let NotWorking (display:(obj -> unit) -> obj list -> unit) =
    display (printfn "%O") [1;2;3]
    display (printfn "%O") ["a";"b";"c"]
like image 29
John Palmer Avatar answered Jan 05 '23 19:01

John Palmer