The F# compiler is trying to generalize f1 and f2 but it has insufficient information thus the value restriction error. Automatic generalization is performed on functions that have explicit arguments. The functions f1 and f1 are not generalizable because they are missing explicit arguments. Notice that in your code you don't need type annotations at all and the four functions will be automatically generalized if you make the arguments of f1 and f2 explicit:

1
2
3
4
5
let q1' f = snd >> f
let f1' _ = q1 id

let q2' = fun f -> snd >> f
let f2' _ = q2' id
By on 11/21/2012 4:19 AM ()

Adding a dummy argument changes the type of the function. Instead of an (int * obj) -> obj I would wind up with an 'a -> (int * obj) -> obj. That's not desirable. q is supposed to be the combinator, and f is supposed to be an actualized function. (In real life, q is a function for generating behavior tree nodes as functions, and each individual f composes a particular behavior like AttackBehavior or HealBehavior.) So, adding a dummy argument is not the way to solve this problem.

Until I find a solution, for now I'm just living with the explicit annotations on f as well as q. But I wish I knew why the F# compiler wasn't using the type information on q to resolve f without explicit annotations.

-Max

By on 11/30/2012 4:33 PM ()

I think the key to a start of response in inside the error message you get with this code,

1
2
let q1 f : int * obj -> obj = snd >> f
let f1 = q1 id

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

The "problem" is here is you're not defining a function f1, but are binding a value to f1
you would have the same problem with this

1
let empty = List.Empty

(Not really sure to know why let empty = [] works, maybe because how the compiler transform computation expression)

so back to the problem, you have two choices, what you used for now, is the second, "add a type annotation"
if you want to use the first, "simply make arguments explicit":

1
2
3
4
5
6
7
8
let q1 f = snd >> f
//val q1: ('a -> 'b) -> ('c * 'a -> 'b)
let f1 explicitArgument = q1 id >> explicitArgument
//val f1: ('a -> b) -> ('c * 'a -> 'b)

//it also work with q2 and f2 (same signature)
let q2 = fun f -> snd >> f
let f2 = fun explicitArgument -> q2 id >> explicitArgument

you could even "mix" the 2 techniques (and see f1 isn't as constrained as q1):

1
2
3
4
let q1 f : int * obj -> obj = snd >> f
//val q1: (obj -> obj) -> (int * obj -> obj)
let f1 explicitArgument = q1 id >> explicitArgument
//val f1: (obj -> 'a) -> (int * 'b -> 'a)

I don't pretend knowing exactly how and why all this but it surely is linked to the difference between defining a function and binding to a value.

Hope it'll help.

By on 11/30/2012 5:47 PM ()

"The "problem" is here is you're not defining a function f1, but are binding a value to f1
you would have the same problem with this."

Maybe you're right about this being what is messing up the type inference. It certainly does have enough information already to infer that 'a = obj (i.e. not a generic value). Thanks for the pointer--I guess I can check the F# spec and see what it says about type inference for binding values.

-Max

By on 12/1/2012 11:48 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper