4

Consider the following:

type T () =
  member x.y = 4

let a =
  let fn () (k: T) = ()
  fn ()

let b =
  let fn () (k: System.IO.Directory) = ()
  fn ()

a fails while b is ok. The error message is:

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

Why and how to fix that?

Oldrich Svec
  • 4,191
  • 2
  • 28
  • 54

3 Answers3

6

The error message itself tells you exactly what you need to do - add a type annotation:

let a : T -> unit = 
  let fn () (k: T) = () 
  fn () 

The reason that you see the error in the first place is that the compiler tries to generalize the definition of a (see this part of the spec), which results in the odd signature that you see in the error message.

The reason that you don't need to do this for b is that System.IO.Directory is sealed, so there is no need to generalize.

kvb
  • 54,864
  • 2
  • 91
  • 133
1

You are facing a value restriction, because a looks like a constant but it returns a function. Have a look at this question:

Understanding F# Value Restriction Errors

One easy way to solve it is adding a variable to the definition of a.

let a x =
  let fn () (k: T) = ()
  fn () x

I don't know why with some types it works, which is the case of b

Community
  • 1
  • 1
Gus
  • 25,839
  • 2
  • 51
  • 76
  • However, adding `x` to the definition changes `a` from a function value to a function, which means that it will not be evaluated during startup. See [F# values, functions and a little bit of both](http://blog.wezeku.com/2010/08/01/values-functions-and-a-bit-of-both) for a full explanation. – John Reynolds Feb 03 '12 at 14:07
0

If T where a record instead of a class, it would work. But for some reason, you have to spell it out for the compiler if T is a class,

type T () =
  member x.y = 4

let a<'U when 'U :> T> =
  let fn () (k: 'U) = ()
  fn ()

let test0 = a<T> (T())  // You can be explicit about T,
let test1 = a (T())     // but you don't have to be.

edit: So I played a bit more with this, and weirdly, the compiler seems to be content with just any type restriction:

type T () =
  member x.y = 4

type S () =
  member x.z = 4.5

let a<'U when 'U :> S> =
  let fn () (k: T) = ()
  fn ()

let test = a (T())     // Is OK
let test = a<T> (T())  // Error: The type 'T' is not compatible with the type 'S'

The type S has nothing to do with anything in the code above, still the compiler is happy to just have a restriction of any kind.

John Reynolds
  • 4,927
  • 4
  • 34
  • 42