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?
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:
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 })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With