views:

140

answers:

2

Can anyone explain why the second example below won't compile?

'Test 2' gives "error FS0670: This code is not sufficiently generic. The type variable ^a could not be generalized because it would escape its scope.". I fail to understand this error message.

// Test 1
type test1<'a> = | A of 'a 
  with
    override t.ToString() = 
      match t with
      | A a -> a.ToString()

// Test 2
type test2<'a> = | A of 'a 
  with
    override t.ToString() = 
      match t with
      | A a -> string a

// Test 3
type test3<'a> = | A of 'a 
  with
    override t.ToString() = 
      match t with
      | A a -> string (a :> obj)
A: 

I imagine it's because string has a signature of (obj -> string) so using string on its own is forcing a to be of type obj. (F# doesn't do implicit casts.)

Massif
`string` [is in fact `'T -> string`](http://msdn.microsoft.com/en-gb/library/ee340491.aspx), so this should work in theory. I don't have a better explanation for the error though.
Tim Robinson
Oh yes, I just stuck string;; into F# interactive to get the type.Presumably actually using string is forcing the compile to force a type on the input, and it's choosing obj as the lowest common denominator?
Massif
+3  A: 

Here's another repro:

let inline f< ^T>(x:^T) = box x

type test4<'a> = | A of 'a  
  with 
    member t.M() =  
        match t with
        | A a -> f a

string is an inline function that uses static type constraints, and the error diagnostics for such functions are sometimes poor. I don't really understand the diagnostic message itself, but the point is, at the call site, we don't know the generic type 'a, which means we can't inline the right version of the call to string (or f in my repro). In e.g. the case where you upcast to obj, we know that we want to inline the obj version of string, so that is ok.

Brian