Распаковка необходима для кортежей, но не для типов

Может кто-нибудь объяснить следующее, это кажется немного противоречивым.

Эта строка кода недействительна:

let l = [("Hi", 1); ("Ho", "One")]

Потому что кортежи разные, string*int vs string*string OK.

Эта строка кода также недействительна:

let (m: (string*obj) list) = [("Hi", 1); ("Ho", "One")]

Я прямо говорю ему, что это список кортежей string*obj, но он не приводит автоматически 1 и «One» к obj. Хорошо и с этим.

Эта строка кода действительна:

let (n: (string*obj) list) = [("Hi", unbox(1)); ("Ho", unbox("One"))]

В этом случае я явно распаковываю 1 и «Один», и это работает.

Вот где я думаю, что вещи становятся немного непоследовательными. Возьмите следующее:

type thing =
    {
        name: string
        value: obj
    }

let (p: thing list) = [{name="Hi"; value=1}; {name="Ho"; value="One"}]

Этот код действителен. Значения 1 и «Один» присваиваются «значению», которое является объектом.

Почему мне не нужно распаковывать член типа, а нужно распаковывать элемент в кортеже?


person Richard Dalton    schedule 31.10.2013    source источник
comment
Да, непоследовательно. F# иногда вводит box. Никогда не вводите unbox/:?> — это небезопасно. Обратите внимание, что ваш пример действительно читается как ("Hi", unbox (box 1)) так же, как ("Hi", box 1).   -  person t0yv0    schedule 31.10.2013


Ответы (1)


Как упоминалось в комментариях, это несколько непоследовательно, но в этом случае на самом деле имеет смысл.

Суть в том, что компилятор никогда не вставляет бокс внутри подвыражения выражения. Он делает это только сразу — при вызове метода или функции (которая принимает obj) или при присвоении значений полям записи. Однако он никогда не вставляет (*) рамку, если это необходимо внутри какого-то более крупного выражения.

Итак, в вашем примере, когда компилятор видит ("Hi", 1) и ("Ho", "One"), он просто создает два кортежа с типами string * int и string * string, а затем терпит неудачу, потому что они не совпадают.

Когда компилятор видит {name="Hi"; value=1}, он определяет, что вы создаете thing, и поэтому он автоматически упаковывает аргумент значения в obj (и тогда вы получаете действительный список вещей).


(*) Единственным исключением является создание массива или списка значений, но это особый случай в компиляторе (который оказывается весьма полезным, но на самом деле не помогает, потому что вам нужно добавить бокс не только внутри списка, но и внутри кортежа внутри списка). Это действительно, хотя:

let (arr:obj list) = [ 1; "hi" ]
person Tomas Petricek    schedule 31.10.2013
comment
Спасибо за это. Это имеет смысл, но на какое-то время заставило меня немного почесать голову. Я так привык к тому, что F# кажется телепатическим в отношении того, что я делаю с типами, что все больше сбивает с толку, когда я пробую что-то умное, а он отказывается подыгрывать. - person Richard Dalton; 01.11.2013