In the following snippet my intention is to convert a System.Object (which could be an FSharpList) to a list of whatever generic type it is holding.
matc
Unfortunately, there's no easy way to do what you want. Type tests can only be used with specific types, and even if the type test passed, the conversion operator :?>
also only works to cast expressions to specific types so the right hand side of your match wouldn't do what you want anyway. You can partially work around this issue using an active pattern:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
let ( |GenericType|_| ) =
(* methodinfo for typedefof<_> *)
let tdo =
let (Call(None,t,[])) = <@ typedefof<_> @>
t.GetGenericMethodDefinition()
(* match type t against generic def g *)
let rec tymatch t (g:Type) =
if t = typeof<obj> then None
elif g.IsInterface then
let ints = if t.IsInterface then [|t|] else t.GetInterfaces()
ints |> Seq.tryPick (fun t -> if (t.GetGenericTypeDefinition() = g) then Some(t.GetGenericArguments()) else None)
elif t.IsGenericType && t.GetGenericTypeDefinition() = g then
Some(t.GetGenericArguments())
else
tymatch (t.BaseType) g
fun (e:Expr<Type>) (t:Type) ->
match e with
| Call(None,mi,[]) ->
if (mi.GetGenericMethodDefinition() = tdo) then
let [|ty|] = mi.GetGenericArguments()
if ty.IsGenericType then
let tydef = ty.GetGenericTypeDefinition()
tymatch t tydef
else None
else
None
| _ -> None
This active pattern can be used as follows:
match o.GetType() with
| GenericType <@ typedefof<list<_>> @> [|t|] -> addChildListUntyped(t,o)
| _ -> addChild(o)
where you've created a variation of addChildList
which takes a type t
and an object o
(with runtime type list<t>
) instead of taking a generic list.
This is a bit clunky, but I can't think of a cleaner solution.
It turns out that either list<'a>
or array<'a>
can be matched as seq<obj>
match o with
| :? seq<obj> -> addChildCollection(o :?> seq<obj>)
| _ -> addChild(o)
I don't really care that it is a list. As long as I can iterate over it.