2

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()
Trident D'Gao
  • 18,973
  • 19
  • 95
  • 159

2 Answers2

7

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".

Community
  • 1
  • 1
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • I don't get how it works: `let f x = toF id x` should not work as there are 2 arguments in `toF id x` while as declared `toF` gets only one parameter `g`. I mean is x just ignored or what? – Trident D'Gao Oct 24 '13 at 17:50
  • `let f x =` defines a function (that happens to return function taking another argument) while `let f = ` defines a value (which is actually a function of two arguments) – Tomas Petricek Oct 24 '13 at 18:06
  • It is really just a simple syntactic condition - it has to be a function (taking at least some argument) – Tomas Petricek Oct 24 '13 at 18:07
  • Tomas, the way you suggested doesn't work either, (as long as we want `fg` to be generic), your example only works because you call `fg` immediately after it is defined with numbers so F# infers that `fg` operates on numbers, if you delete `fg 1` and `fg 2` you will get the very same error – Trident D'Gao Oct 25 '13 at 21:13
1

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
kvb
  • 54,864
  • 2
  • 91
  • 133
  • Nice... It seems like a function isn't a first-class citizen in F# since it cannot be returned and assigned to a value. – Trident D'Gao Oct 25 '13 at 22:04
  • @AlekseyBykov where do you get that idea? Of course you can assign a function to a value, that's what you are doing all the time and that's the whole essence of F#. – Abel Nov 03 '14 at 02:33