Interacting with generic types and boxing/unboxing

covariance, f#, generics

Solution

When you call `addToMyTupleList<'a>`, the concrete type of `'a` is statically known by the compiler (that is, you're calling `addToMyTupleList<int>`, `addToMyTupleList<float>`, etc.). By contrast, when you're trying to do the unboxing within `Seq.iter`, you're hoping that `'a` will be determined based on the runtime type of the argument, which isn't how the F# type system works.

As I see it, you have a few options:

- Use a type test, as Daniel suggests.

- Instead of storing the raw values in the list, store the outputs you want to generate (that is, use a `string list` where you call `sprintf` as you're putting things in.

Be a bit more precise with the types you're storing in your list. Encode the type `∃'a.string * 'a` (that is, it's a pair consisting of a string and an `'a`, for some unknown `'a`) and store a list of these. In a language like Haskell this isn't too bad, but faithfully encoding it in F# is ugly/confusing:

type ExPair<'x> =
    abstract Apply : string * 'a -> 'x
type ExPair =
    abstract ForAll : ExPair<'x> -> 'x

let pack p = { new ExPair with 
                    member __.ForAll<'x>(e:ExPair<'x>) : 'x = e.Apply p }

let myList = [pack ("integer", 3)
              pack ("float", 3.0)
              pack ("string", "string")
              pack ("tuple", (3, "integer"))]

myList
|> List.map (fun e ->
    e.ForAll { new ExPair<string> with member __.Apply(s,x) = sprintf "%s,%A" s x })

Problem

I'm interested in understanding more about how the following is possible. Given a collection constrained to type obj and the following function: ``` let myList = new ResizeArray<obj>() let addToMyListTuple<'a> (item : string * 'a) = let boxed = box item let unboxed = unbox<string * 'a> boxed let item1 = match unboxed with first, _ -> first Console.WriteLine(sprintf "%A" item1) myList.Add(boxed) ``` Interacting with these 2 gives the expected results and the string part of the tuple can be consumed regardless of the associated type in the second part. ``` addToMyListTuple("integer", 3) addToMyListTuple("float", 3.0) addToMyListTuple("string", "string") addToMyListTuple("tuple", (3, "integer")) ``` However, what I was hoping would be possible was this where I would be able to interact with items in the list at a later time and unbox the obj in such a way that accessing the string part of the tuple would be possible. ``` myList |> Seq.iter(fun x -> let unboxed = unbox<string * 'a> x let item1 = match unboxed with first, _ -> first Console.WriteLine(sprintf "%A" item1) ) ``` Running this gives me the compile time warning ``` This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'obj'. ``` and the runtime exception ``` System.InvalidCastException: Unable to cast object of type 'System.Tuple`2[System.String,System.Int32]' to type 'System.Tuple`2[System.String,System.Object]'. ``` Is there any other way to accomplish this behavior?

Original source