Определение исключений с кортежем в качестве аргумента - PullRequest
5 голосов
/ 15 октября 2010

Я пытаюсь определить исключение в OCaml, которое принимает пару списков кортежей в качестве аргумента. Однако эта ситуация не работает?

# exception Foo of string list * string list;; 
exception Foo of string list * string list
# let bar = (["a"], ["b"; "c"; "d"]);;
val bar : string list * string list = (["a"], ["b"; "c"; "d"])
# raise(Foo bar);;
Error: The constructor Foo expects 2 argument(s),
       but is applied here to 1 argument(s)

Однако, если я это сделаю, это будет работать

# raise (Foo (["a"], ["b"; "c"; "d"]));;
Exception: Foo (["a"], ["b"; "c"; "d"]).

В чем дело? Спасибо!

Ответы [ 3 ]

11 голосов
/ 15 октября 2010

Вы смотрите на это неправильно (хотя я не буду винить вас: сначала это довольно удивительно). Вам может показаться, что конструкторы следуют синтаксису Name of type, где часть типа соответствует синтаксису обычного типа (что позволяет содержать кортежи).

В действительности кортежи и конструкторы следуют точно такому синтаксису: конструктор - это просто кортеж с именем перед ним:

tuple/constructor == [name of] type [* type] [* type] ...

Таким образом, * в определении конструктора не являются частью синтаксиса кортежа, они являются частью синтаксиса конструктора. Вы буквально определяете конструктор как это имя, за которым следует N аргументов вместо этого имени, за которым следует аргумент, который является кортежем .

Причина этой тонкой разницы в поведении связана с производительностью. Прямо сейчас кортежи и конструкторы представлены в памяти так:

[TYPE] [POINTER] [POINTER] [POINTER]

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

[TYPE] [POINTER]
           |
           v
          [TYPE] [POINTER] [POINTER] [POINTER]

Это позволило бы использовать немного больше памяти, потребовать вдвое больше выделений при использовании конструктора и снизить производительность кортежей сопоставления с образцом (из-за дополнительной разыменования). Чтобы сохранить максимальную производительность, name of type * type представлен с использованием первого шаблона, и вам нужно явно набрать name of (type * type), чтобы отрезать * от of и, таким образом, использовать второй шаблон.

Обратите внимание, что оба шаблона доступны через один и тот же синтаксис сопоставления и построения шаблонов: name (arg,arg). Это означает, что вывод типа не может вывести шаблон на основе использования. Это не проблема для обычных конструкторов, которые всегда определяются в определении типа, но это приводит к тому, что варианты (которые не нуждаются в предварительном определении) автоматически возвращаются к версии second .

Дополнительное чтение в памяти представления типов здесь .

5 голосов
/ 15 октября 2010

В этом отношении конструкторы исключений OCaml похожи на обычные конструкторы:

Constr(1,2,3) - это особая синтаксическая конструкция, в которой тройка не встречается. С другой стороны, тройка встречается в Constr((1,2,3)). Реализация соответствует этому поведению: Constr(1,2,3) выделяется как один блок, а Constr((1,2,3)) как два блока, один из которых содержит указатель на другой (тройной). В представлении времени выполнения Constr(1,2,3) нет тройки, на которую можно получить указатель, а если вам нужен, вам нужно выделить новый.

Примечание: Constr(((1,2,3))) эквивалентно Constr((1,2,3)). В Constr(((1,2,3))) средние скобки интерпретируются как обход выражения (1,2,3), а круглые скобки вокруг выражения забываются в абстрактном синтаксическом дереве.

4 голосов
/ 15 октября 2010

Foo является конструктором исключения с 2 параметрами.Вам придется разложить кортеж и передать в него каждую часть.

# exception Foo of string list * string list;;
exception Foo of string list * string list
# let bar = (["a"], ["b"; "c"; "d"]);;
val bar : string list * string list = (["a"], ["b"; "c"; "d"])
# let a, b = bar in raise (Foo (a, b));;
Exception: Foo (["a"], ["b"; "c"; "d"]).

Если вы хотите использовать кортеж в качестве единственного параметра, вы должны определить исключение с помощью паренов и передать кортеж внутрь.

# exception Foo of (string list * string list);;
exception Foo of (string list * string list)
# let bar = (["a"], ["b"; "c"; "d"]);;
val bar : string list * string list = (["a"], ["b"; "c"; "d"])
# raise (Foo bar);;
Exception: Foo (["a"], ["b"; "c"; "d"]).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...