Zaid Ajaj
Zaid Ajaj

Reputation: 680

How to convert a list of objects to a primitive data type?

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

Answers (2)

Tomas Petricek
Tomas Petricek

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

Mark Seemann
Mark Seemann

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

Related Questions