使用元组作为参数定义异常

时间:2010-10-15 07:41:23

标签: ocaml

我正在尝试在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 个答案:

答案 0 :(得分:11)

你看错了(尽管我不会责怪你:起初这是非常令人惊讶的)。在您看来,构造函数遵循语法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)。这意味着类型推断不能基于用法推断出模式。这对于常规构造函数来说没有问题,它总是在类型定义中定义,但它会导致变体(不需要初步定义)自动回退到第二个版本。

关于类型here的内存表示的补充阅读。

答案 1 :(得分:5)

在这方面,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),并且在抽象语法树中忘记了表达式周围的括号。

答案 2 :(得分:4)

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

如果您希望使用元组作为单个参数,则必须使用parens定义异常并传入元组。

# 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"]).