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" ]