4

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?

Patrick McDonald
  • 64,141
  • 14
  • 108
  • 120

2 Answers2

6

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.

Community
  • 1
  • 1
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
5

This works as well

let NotWorking (display:(obj -> unit) -> obj list -> unit) =
    display (printfn "%O") [1;2;3]
    display (printfn "%O") ["a";"b";"c"]
John Palmer
  • 25,356
  • 3
  • 48
  • 67
  • +1 This is definitely the simplest way to solve it in practice. I can't imagine a scenario where you'd actually need a generic `display` function as an argument. – Tomas Petricek Dec 08 '11 at 01:42
  • Thanks for your answer, it definitely solved the problem as stated, however I had oversimplified my problem and ran into covariance problems (I think) in my real code. This method is definitely much simpler and in general should solve a large proportion of similar problems. – Patrick McDonald Dec 08 '11 at 22:39