Question

When I try to curry a function with the last parameter as a generic type the compiler forces the generic type into the first concrete type it finds, thus losing its generic property for subsequent calls in scope. Example:

type Bar =
    | String
    | Integer
    | Boolean

let foo (a: String)(b: 'm) =
    printfn "%A - %A" a b

let foobar (a: String)(bar: Bar) =
    let fooWithA= foo a
    match bar with
    | String -> fooWithA "String"
    | Integer -> fooWithA 42
    | Boolean -> fooWithA true

Here the last 2 lines give me a compiler error stating that the function is expecting a String. However, if I wrap the function with a helper class I can make it work like this:

type FooHelper(a:String) =
    member this.foo (b: 'm) =
        printfn "%A - %A" a b

let foobar (a: String)(bar: Bar) =
    let fooHelperWithA= FooHelper a
    match bar with
    | String -> fooHelperWithA.foo "String"
    | Integer -> fooHelperWithA.foo 42
    | Boolean -> fooHelperWithA.foo true

That won't give me a compiler error. Then again if I try to us the function directly I get compilation error again:

let foobar (a: String)(bar: Bar) =
    let fooHelperWithA= FooHelper(a).foo
    match bar with
    | String -> fooHelperWithA "String"
    | Integer -> fooHelperWithA 42
    | Boolean -> fooHelperWithA true

The last 2 lines throw a compilation error.

Is this an intended behavior or is this a bug? If this is how it should work, can someone explain why? this is really confusing to me.

Was it helpful?

Solution

So this is a classic value restriction problem. It is more apparent if you just do this block of code:

let foo (a: String)(b: 'm) =
    printfn "%A - %A" a b 
let fooWithA= foo  "hello"

This will give the following error:

error FS0030: Value restriction. The value 'fooWithA' has been inferred to have generic type val fooWithA : ('_a -> unit)
Either make the arguments to 'fooWithA' explicit or, if you do not intend for it to be generic, add a type annotation.

The problem is that fooWithA is not a proper function and as a result, it is not allowed to have a generic type.

So in the first example, the compiler sees that you are first calling it with a string, so it makes that specialization, which then fails for the integer and boolean cases.

In the second example you have implicitly created a function which can be made generic so all is good.

In the third example, you have the same problem as the first, you are binding the function to a value so it can't be generic.

The simplest solution to all this is to always use a function like so

let fooWithA a = foo "hello" a
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top