Search code examples
genericsf#partial-application

F# partial application function wrapper does not keep return type generic for parameter function


TL DR: I am trying to create a function wrapper. The wrapped function takes no parameter but returns a value.

The reason I want to do this is to create a function similar to "lock" but for a semaphore.

Here it is

let functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

Signature : (string -> (unit -> 'a) -> 'a)
So far, all good

Now let's enrich functionWrapper with a parameter

let functionWrapperFoo = functionWrapper "Foo"

Signature : ((unit -> obj) -> obj)
For a reason, 'a becomes obj here..

Now I finally want to use this function to wrap another one.
But the type inference will change the signature of functionWrapperFoo into ((unit -> int) -> int) which prevents from using the function with other types afterward.

let resultFooInt =
    functionWrapperFoo (
        fun _ ->
            (42)
        )


// Because I am using a string as a return type now, this one bellow won't compile
let resultFooString =
    functionWrapperFoo (
        fun _ ->
            "42"
        )

Why I can't keep this function generic after the first partial application ?

I did find a workaround but I really do not understand the reason for this.

I've seen some post mentioning something similar about partial application but in my case my wrapped function does not have parameters
Keeping partially applied function generic

Full original code

let functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

let functionWrapperFoo = functionWrapper "Foo"
let resultFooInt =
    functionWrapperFoo (
        fun _ ->
            (42)
        )
let resultFooString =
    functionWrapperFoo (
        fun _ ->
            "42"
        )

Workaround

let functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

let functionWrapperFoo() = functionWrapper "Foo"

let resultFooInt =
    functionWrapperFoo() (
        fun _ ->
            (42)
        )
    
let resultFooString =
    functionWrapperFoo() (
        fun _ ->
            "42"
        )

Solution

  • This is an issue known as value restriction. There is a fairly detailed explanation of this in this article on automatic generalization in F# and there are also good existing answers on SO.

    The basic issue is that F# does not let you define generic syntactic values. Your functionWrapperFoo is defined as a value, i.e. using let value = <expr>. In this case, the value cannot be generic, even if the expression <expr> actually evalautes to a function.

    Your workaround works, because it makes the syntactic declaration into a function declaration using let func arg = <expr>. Here, the compiler knows for sure that it is going to be a function.

    As mentioned by Tom, a better workaround is to just add a parameter to your functionWrapperFoo definition and pass it to functionWrapper - that way, it will be syntactically defined as a function and automatic generalization will make it generic.