Can someone explain the following, it seems a little inconsistent.
This line of code is invalid:
let l = [("Hi", 1); ("Ho", "One")]
Because the tuples are different string*int vs string*string OK.
This line of code is also invalid:
let (m: (string*obj) list) = [("Hi", 1); ("Ho", "One")]
I explicitly tell it it's a list of string*obj tuples, but it doesn't automatically cast the 1 and "One" to obj. OK with that too.
This line of code IS valid:
let (n: (string*obj) list) = [("Hi", unbox(1)); ("Ho", unbox("One"))]
In this case I explicitly unbox 1 and "One" and it works.
Here's where I think things get a little inconsistent. Take the following:
type thing =
{
name: string
value: obj
}
let (p: thing list) = [{name="Hi"; value=1}; {name="Ho"; value="One"}]
That code is valid. The values 1 and "One" are both assigned to the 'value' which is an obj.
Why do I not need to unbox for a member of a type, but I for an item in a tuple?
As mentioned in the comments, this is somewhat inconsistent, but it actually makes some sense in this case.
The main point is that the compiler never inserts boxing anywhere inside a sub-expression of an expression. It only does this immediately - when calling a method or function (that takes obj
) or when assigning values to record fields. However, it never (*) inserts boxing when this would be needed inside some larger expression.
So in your example, when the compiler sees ("Hi", 1)
and ("Ho", "One")
, it just creates two tuples with types string * int
and string * string
- and then it fails because these do not match.
When the compiler sees {name="Hi"; value=1}
, it figures out that you are creating a thing
and so it boxes the value argument to obj
automatically (and then you end up with a valid list of things).
(*) The only exception is when you create an array or list of values, but this is an ad-hoc special case in the compiler (which happens to be quite useful, but does not really help you, because you'd need to add boxing not just inside a list, but inside a tuple inside a list). This is valid though:
let (arr:obj list) = [ 1; "hi" ]