views:

86

answers:

3
match value with
| :? list<#SomeType> as l -> l //Is it possible to match any list of a type derived from SomeType?
| _ -> failwith "doesn't match"
+1  A: 

No, it's unfortunately not possible to do something like this - the CLR doesn't provide any efficient way of doing that kind of type test. See How to cast an object to a list of generic type in F# and F# and pattern matching on generics in a non-generic method implementing an interface for a few (rather ugly) solutions.

kvb
+2  A: 

As already pointed out, there is no way to do this directly (pattern matching can only bind values, but it cannot bind new type variables). In addition to the (more general) workaround by kvb you can use the fact that all collections implement non-generic IEnumerable, so you can check for this type:

match box value with 
| :? System.Collections.IEnumerable as l when 
     // assumes that the actual type of 'l' is 'List<T>' or some other type
     // with single generic type parameter (this is not fully correct, because
     // it could be other type too, but we can ignore this for now)
     typedefof<SomeType>.IsAssignableFrom
       (value.GetType().GetGenericArguments().[0]) -> 
   l |> Seq.cast<SomeType>
| _ -> failwith "doesn't match"

The code tests whether the value is a non-generic IEnumerable and whether the type parameter is subtype of SomeType. In that case, we got a list of some derived type, so we can cast it to a sequence of SomeType values (this is slightly different than working with list of values of the derived types, but it shouldn't matter for practical purposes).

Tomas Petricek
A: 

I later needed something similar for matching Lazy instances. Here's my solution, in case anyone finds it helpful.

let (|Lazy|_|) (value : obj) =
    if box value <> null then
        let typ = value.GetType()
        if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<Lazy<_>> then
            Some(typ.GetGenericArguments().[0])
        else None
    else None

Usage:

match value with
| Lazy typ when typeof<SomeType>.IsAssignableFrom(typ) -> (value :?> Lazy<_>).Value
| _ -> failwith "not an instance of Lazy<#SomeType>"
Daniel