Match patterns to a typical type using a flexible type parameter

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" 
+6
pattern-matching f #
source share
5 answers

No, unfortunately, it is impossible to do something like this - the CLR does not provide an effective way to run this type of test. See How to pass an object to a generic type list in F # and F # and pattern matching on generics into a non-generic method that implements an interface for several (rather ugly) solutions.

+3
source share

As already indicated, there is no way to do this directly (pattern matching can only bind values, but cannot bind new type variables). In addition to the (more general) kvb workaround, you can use the fact that all collections implement non-general IEnumerable , so you can check 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 checks to see if this value is not generic IEnumerable and whether the type parameter is a subtype of SomeType . In this case, we got a list of some derived type, so we can apply it to a sequence of SomeType values โ€‹โ€‹(this is slightly different from working with a list of values โ€‹โ€‹of derived types, but it does not matter for practical purposes).

+8
source share

Later, I needed something similar for matching Lazy instances. Here is my solution, in case someone finds it useful.

 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 

Using:

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

Not the cleanest, but effective:

 let matchType<'T> () = try let o = Activator.CreateInstance<'T> () match box o with | :? Type1 -> printfn "Type1" | :? Type2 -> printfn "Type2" | _ -> failwith "unknown type" with | ex -> failwith "%s" (ex.ToString()) 
+1
source share

According to specification F # 2.0 , par. 14.5.2 (Solving Subtype Constraints), this will not work because: "General F # types do not support covariance or contravariance."

0
source share

All Articles