Reputation: 19762
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
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
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