Question

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?

Was it helpful?

Solution

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" ]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top