Trident D'Gao
Trident D'Gao

Reputation: 19762

Can't create generic partial application of a function in F#

I am looking for a way to fix this very certain situation: I have a function-factory toF that takes a function-parameter g and based on it creates a resulting function f

let toF g = 
    let f x = g x
    f
let f = toF id

The problem is that I get a

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

I can add type annotations (which I am not eager to do) or alternatively I can rewrite it like this:

let f' g x = g x
let f x = f' id x

I don't like doing it this way because if I do then every time I call f I am making another call to f' specifying g along the way. While the first example keeps g in the closure and requires just one call.

UPDATE (for Tomas)

I have tried what you suggested.

let toF g = 
    printfn "Creating f using g"
    let f x =
        printfn "x: %A" x
        g x
    f
let f x = toF id x

let ``test``() =
    1 |> f |> f |> ignore

What basically is happening is that every time I make a call to the function f it first calls toF id getting a composed function and only then calls that composed function on x.

Creating f using g
x: 1
Creating f using g
x: 1

So essentially the composition is created on every call to f via subsequent call to toF. But this is exactly what I was trying to avoid. By defining let f = toF id I was hoping to get a closure one sigle time and then be able to call it immediately. So the output I am expecting would be:

Creating f using g
x: 1
x: 1

UPDATE 2

The following doesn't work either for the very same reason:

let toF g = 
    printfn "Creating f using g"
    let f x =
        printfn "x: %A" x
        g x
    f
let f() = toF id
let fg = f()

Upvotes: 2

Views: 502

Answers (2)

kvb
kvb

Reputation: 55195

The easiest solution is to just add a type annotation. Assuming that there's only one real type you care about, this is completely straightforward:

let toF g = 
    let f x = g x
    f
let f : _ -> int = toF id

If you really need to call f at different types, then you can wrap it in a generic type:

type F<'t>() =
    static member val f : _ -> 't = toF id

let blah = F.f "blah"
let one = F.f 1

Upvotes: 1

Tomas Petricek
Tomas Petricek

Reputation: 243126

You just need to make f a syntactic function:

let toF g = 
    let f x = g x
    f
let f x = toF id x

When f is not syntactically a function (taking parameter) but a value, you hit the "value restriction" error. I'm not going to try to explain it here, because there is already great info in previous posts like: Understanding F# Value Restriction Errors.

EDIT - If you want to make sure that g gets called only once (but still want the code to be generic) then the easiest way is to add unused unit parameter (to make it a function) and then call it once (which determines the generic parameters) and use the result multiple times:

let toF g = 
    let f x = g x
    f
let f () = toF id

let fg = f ()
fg 1
fg 2

This is sadly needed, because having a function that is generic, but is returned by some computation would actually create a subtle hole in the type system - that's the reason for the "value restriction".

Upvotes: 7

Related Questions