Yep, this is a known limitation in 1.9.3.14, now fixed and the fix will be in the next release of F#

For now use

1
2
3
4
 
match x with 
| _ when x = 0I -> ...
| _ when x = 1I -> ...

etc.

Kind regards

don

By on 2/7/2008 2:03 PM ()

Thanks Don!

Now I'm trying to do Type Testing via Pattern Matching with Generics but the code it's not working as expected.

I want to get a match for any subtype of seq of 'a

1
2
3
4
 let foo (a:obj) =
  match a with
  | :? #seq<'a> -> "Works!"
;;

But I get:

This construct causes code to be less generic than indicated by the type annotations. The type variable 'b has been constrained to be type 'seq<'a>'.

This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'obj'.

By on 2/14/2008 9:42 PM ()

This is a limitation with .NET generics. The real explainiton is rooted in type theroy that I don't understand, but here's how I like to think of the probablem: the compiler has no where to put the type variable 'a, it can't go on the method as it's parameters as this would mean calls of the method would have to specific it up front, so the code it generates is IEnumerable<object> rather than IEnumerable<'a>. The .NET "is" test does not consider IEnumerable<object> to be the same as IEnumberable<int> ("is" is the C# equivalant of :?, which is the IL instuction "isinst"). This leads to the following slightly bizar results (which the warning is trying to make you aware of):

1
2
3
4
5
6
7
8
#light
let foo (a:obj) =
  match a with
  | :? seq<'a> -> "seq"
  | _ -> "not seq"
  
foo (box [1; 2])  // result "not seq"
foo (box [ box 1; box 2]) // result "seq"

Decompiling the code to C# using reflector may also help you understand whats going on.

Ironically if you want to generically match all IEnumerable<'a> types its easiest to use the non-generic version of IEnumerable:

1
2
3
4
5
6
7
let foo2 (a:obj) =
  match a with
  | :? System.Collections.IEnumerable -> "seq"
  | _ -> "not seq"
  
foo2 (box [1; 2])  // result "seq"
foo2 (box [ box 1; box 2]) // result "seq"

Hope that helps!

Cheers,
Rob

By on 2/15/2008 4:39 AM ()

Thanks Robert! But now I'm more confused.

I have changed the return and I get a totally different result. I dont get the warnings (everything seems right)

1
2
3
4
5
let foo (x:obj) = 
  match x with 
  | :? seq<'a> as s -> s 
  | _ -> failwith "not a seq"
;;

But...

1
foo (box {1..3});;

stdin(0,0): error: FS0030: Value restriction. Type inference has inferred the signature
val it : seq<'_a>
Either define 'it' as a simple data term, make it a function, or add a type constraint to instantiate the type parameters.
stopped due to error

By on 2/17/2008 6:25 PM ()

Diego, what are you trying to do, exactly? It seems like you're mixing up what can be done at compile time and what can be done at runtime. I think that Robert pointed out that the compiler can't make anything of seq<'a> in this context. Since you've effectively downcast the argument's type to System.Object, you've deprived the compiler of the information it would need to generate the IL for the attempted downcast to IEnumerable<IDontKnowWhat>. Think about how you'd have to do this in C#: you'd have to cast x either to IEnumerable, as Robert suggested (since implementations of IEnumerable<_> are guaranteed to implement IEnumerable as well); to IEnumerable<ClassKnownInAdvance>, or use reflection to determine that x implements IEnumerable<'a>, and then what 'a is (and then generate some code yourself). Which is to say, in C# you can't do what you seem to be trying to do, and you probably can't do it in F# either.

By on 2/18/2008 6:58 AM ()

Well... I'm just playing around with the syntax. I'm not trying to make things work. Instead I'm wondering why, _just_ changing the return I loose the warnings.

If the reason to getting the first warning lies on type theory. What's the difference in the aforementioned cases?

By on 2/18/2008 11:39 AM ()

When you're getting confused with code that uses dynamic type tests and generics, it is sometimes useful to ask -- as SlideGuitarist suggests -- what would C# do?

In C# your function would be

1
2
3
4
5
IEnumerable<T> foo<T>(object x) {
  IEnumerable<T> s = x as IEnumerable<T>;
  if(s != null) return s;
  else throw FailureException("not a seq");
}

Now you are going to call foo. What are you going to pass as the type parameter?

1
foo<?>(/*{1..3}*/);

In F#, the compiler usually infers the type parameters to pass to a generic method, so you don't have to write them explicitly as in C#. However in this case it is not possible to infer what the type parameter is, so F# can't do magic for you.

You can work around this problem by explicitly passing a type parameter in F#.

1
foo<int> (box {1..3})
By on 2/19/2008 12:52 AM ()

one addition to gneverov post that in C# it is also possible not to infer them explicitly if the compiler can figure out.

But the same problem might occur, sometimes the compiler can't figure out.

1
2
foo<string>("t");
foo("t");

Those are the same as long as the compiler can infer them.

By on 2/19/2008 5:40 AM ()

Thanks! now I think I understand.

I don't know much of C#. F# is the first .Net language that I have met so I cant think about "What would C# do". Anyway, gneverov, your example have been really clarifying.

By on 2/19/2008 1:27 PM ()

My point was actually that you're doing something that you should never need to do in F#. If you have a function foo (o : obj) = ..., then you will have to use reflection yourself to find out more about the type of the object. In other words, it's not generic, because you added a type annotation to o! If you made it more generic, then you'd have no need to check the type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
> let foo (s : #seq<'a>) =
-   s.GetType().FullName;;
val foo : #seq<'a> -> string

> foo ([1;2;3]);;
val it : string
= "Microsoft.FSharp.Collections.List`1+_Cons[[System.Int32, mscorlib, Version=2.
0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"

> open System.Collections.Generic;;

> let l = new List<int>();;
val l : List<int>

> l.AddRange([1..10]);;
val it : unit = ()

> l;;
val it : List<int> = seq [1; 2; 3; 4; ...]

> foo(l);;
val it : string

= "System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=2.0.0.0, C
ulture=neutral, PublicKeyToken=b77a5c561934e089]]"
>

If what you really want is just to write a function that gives some information about the type in which the function was parametrized, you'd probably use the Microsoft.FSharp.Reflection namespace (see p. 137-38 in Bob Pickering's Foundations of F#, among other sources).

By on 2/20/2008 6:42 AM ()

Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> open Microsoft.FSharp.Reflection;;
> let foo (s : #seq<'a>) =
-     Value.GetTypeInfo(s);;
val foo : #seq<'a> -> TypeInfo

> foo l;;
val it : TypeInfo

= ObjectType
   System.Collections.Generic.List`1[System.Int32]
     {Assembly = mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a
5c561934e089;
      AssemblyQualifiedName = "System.Collections.Generic.List`1[[System.Int32,
mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], m
scorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
      Attributes = AutoLayout, AnsiClass, Class, Public, Serializable, BeforeFie
ldInit;...
By on 2/20/2008 6:54 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