Reputation: 680
I have this dicriminated union that represents a primitive type:
type Expr =
| Int of bigint
| Real of float
| Symbol of string
| Bool of bool
| List of Expr list
And I am writing a function list : obj list -> Expr list
that will check for the types of objects and converts them to Expr
's accordingly:
let rec list : (obj list -> Expr list) = function
| head::tail ->
match head with
| :? string as x'-> Symbol (x') :: list tail
| :? int as x'-> Int(bigint x') :: list tail
| :? bigint as x' -> Int x' :: list tail
| :? bool as x' -> Bool x' :: list tail
| :? float as x' -> Real x' :: list tail
| :? Expr as x' -> x' :: list tail
| :? list<obj> as x' -> List(list x') :: list tail
| _ -> []
The case | :? list<obj> as x' -> List(list x') :: list tail
doesn't seem to match when calling this function on a nested list like this: list [1;[2;1]]
this compiles perfectly but returns an error saying that the match cases weren't complete, it seems that it is trying to mach list<int>
with the cases and it isn't finding it. I expected that list<obj>
would match lists of any type 'a
but thats not the case. What pattern should I write to have it match a list of any type? The function works perfectly for non-nested lists of objects.
Upvotes: 2
Views: 615
Reputation: 243041
There is no way you can use pattern matching to check that an object is a value of a generic type with unspecified generic arguments. You can check whether it is list<int>
or list<obj>
, but it would have to be exactly this type - so list<int>
won't be matched when you check for a type using :? list<obj>
(also, you can write :? list<_>
, but the compiler will just fill the _
with obj
)
If you only care about collections, you can however use the non-generic System.Collections.IEnumerable
interface which all collections (lists, arrays, etc.) implement:
let rec list : (obj list -> Expr list) = function
| head::tail ->
match head with
| :? string as x'-> Symbol (x') :: list tail
// Other cases you're already covering
| :? float as x' -> Real x' :: list tail
| :? Expr as x' -> x' :: list tail
| :? System.Collections.IEnumerable as x' ->
// It is a collection and we can get its elements as `obj` values!
let nested = x' |> Seq.cast |> List.ofSeq |> list
List(nested) :: list tail
| _ -> []
Upvotes: 5
Reputation: 233135
The pattern match is incomplete because head
is of the type obj
. Cases not covered are, for example null
, obj ()
, 1L
(64-bit integer), System.Version ()
, System.Guid("DAB51019-DF69-4547-BC3B-5CE06BE22A7B")
, [| 1; 2 |]
, etc.
There are an unbounded number of cases not handled, because obj
means anything, including custom types.
What pattern should I write to have it match a list of any type?
You can always use the wildcard pattern _
, but only you know what should happen in this case.
Upvotes: 2