Dmitry A. Grechka
Dmitry A. Grechka

Reputation: 80

FSharp pattern that matches List type

I want to match the object obj1 of type "obj" according to its actual type. The problem is that the type check pattern for list type (the second one in the example below) does not match F# lists.

let obj1 = [1;2;3] :> obj
match obj1 with
    | :? System.Array as a -> printfn "it's an array: %A" a
    | :? List<_> as l -> printfn "It's a list: %A" l
    | _ -> printfn "other type"

Outputs "other type", while I expect it to be "It's a list: [1;2;3]"

How to check for list type properly?

Upvotes: 4

Views: 1686

Answers (3)

Mark Seemann
Mark Seemann

Reputation: 233150

Daniel Fabian has already explained the problem in his answer.

One way to implement his solution is with an Active Pattern:

let (|IsList|_|) (candidate : obj) =
    let t = candidate.GetType()
    if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<list<_>>
    then Some (candidate :?> System.Collections.IEnumerable)
    else None

You can now change the match to use this active pattern:

let obj1 = [1;2;3] :> obj
match obj1 with
    | :? System.Array as a -> printfn "it's an array: %A" a
    | IsList l -> printfn "It's a list: %A" l
    | _ -> printfn "other type"

This prints:

> It's a list: [1; 2; 3]

Upvotes: 4

Daniel Fabian
Daniel Fabian

Reputation: 3838

obj : [any type]

What you need to do, is distinguish if your obj is a generic type. Get the type of it by means of .GetType() there you'll find the appropriate property.

obj : [some type of the form T<'T1, 'T2, ..., 'Tn> for concrete types T1, T2, ..., Tn]

If it is, then you get the generic type definition by using the appropritate method on the type you got by means of .GetType()

obj : [some type of the form T<'T1, 'T2, ..., 'Tn> with T1, T2, ..., Tn not bound yet]

and this type you now can compare to typedefof<_ list> (_ list is inferred to be obj list, but typedefof as opposed to typeof does the getting of generic type definition for you already).

The final code looks something like this (pseudo-code)

let ty = obj.GetType()
match obj with
| unknownType when not ty.IsGenericType -> "some non-generic type, probably a concrete collection"
| list when ty.GetGenericTypeDefinition() = typedefof<_ list> -> "list"
| array when ty.GetGenericTypeDefinition() = typedefof<_ array> -> "array"
| _ -> "something else, i.e. a generic type that we don't know"

Upvotes: 3

ntr
ntr

Reputation: 544

I would not call List<_> as F# list - it is .NET framework type. You can match it with (int list) type - it is F# list:

match obj1 with | :? (int list) as l -> 1 | _ -> 0

Upvotes: -2

Related Questions