Per标准ML的定义(修订版):
其思想是,对非扩展表达式的动态求值既不会生成异常,也不会扩展内存域,而对扩展表达式的求值可能会。
[§4.7,p19;强调矿]
我在网上找到了很多关于引用单元格部分的信息,但几乎没有关于异常部分的信息。(一些消息来源指出,多态性结合仍然有可能引发Bind
,这种不一致性可能会产生类型理论和/或实现后果,但我不确定这是否相关。)
我已经能够想出一个与异常相关的不确定性,如果我没有错的话,它只是通过值限制来防止的;但这种不确定性并不取决于引发一个异常:
local
val (wrapAnyValueInExn, unwrapExnToAnyType) =
let exception EXN of 'a
in (EXN, fn EXN value => value)
end
in
val castAnyValueToAnyType = fn value => unwrapExnToAnyType (wrapAnyValueInExn value)
end
那么,有人能告诉我定义是什么意思吗?为什么它提到例外?
("生成异常"是否可能意味着生成一个异常名称,而不是生成一个例外数据包?)
我不是类型理论家或形式语义学家,但我认为我从操作的角度理解定义的目的。
ML异常的生成意味着,每当流的控制两次到达同一个异常声明时,就会创建两个不同的异常。这些不同的对象不仅在内存中,而且在扩展上也是不相等的:我们可以通过与异常构造函数的模式匹配来区分这些对象。
[顺便说一句,这显示了ML异常和大多数其他语言中的异常之间的重要区别。在ML中,可以在运行时创建新的异常类。]
另一方面,如果你的程序两次构建相同的整数列表,你可能在内存中有两个不同的对象,但你的程序无法区分它们。他们在广义上是平等的。
作为为什么生成异常有用的一个例子,可以考虑MLton的通用类型的示例实现:
signature UNIV =
sig
type univ
val embed : unit -> { inject : 'a -> univ
, project : univ -> 'a option
}
end
structure Univ :> UNIV =
struct
type univ = exn
fun 'a embed () =
let
exception E of 'a
in
{ inject = E
, project = fn (E x) => SOME x | _ => NONE
}
end
end
如果ML没有值限制,这个代码将导致一个巨大的类型安全漏洞:
val { inject = inj1, project = proj1 } = Univ.embed ()
val { inject = inj2, project = proj2 } = Univ.embed ()
(* `inj1` and `proj1` share the same internal exception. This is
* why `proj1` can project values injected with `inj1`.
*
* `inj2` and `proj2` similarly share the same internal exception.
* But this exception is different from the one used by `inj1` and
* `proj1`.
*
* Furthermore, the value restriction makes all of these functions
* monomorphic. However, at this point, we don't know yet what these
* monomorphic types might be.
*)
val univ1 = inj1 "hello"
val univ2 = inj2 5
(* Now we do know:
*
* inj1 : string -> Univ.univ
* proj1 : Univ.univ -> string option
* inj2 : int -> Univ.univ
* proj2 : Univ.univ -> int option
*)
val NONE = proj1 univ2
val NONE = proj2 univ1
(* Which confirms that exceptions are generative. *)
val SOME str = proj1 univ1
val SOME int = proj2 univ2
(* Without the value restriction, `str` and `int` would both
* have type `'a`, which is obviously unsound. Thanks to the
* value restriction, they have types `string` and `int`,
* respectively.
*)
Eduardo León的回答是,定义确实指的是这一点,并引入了短语"生成异常"。我对他的答案投了赞成票,但我将其单独发布,因为我觉得他的答案在某种程度上是从错误的方向来回答这个问题的:大部分答案都是对这个问题已经预设的事情的阐述。]
"生成异常"是否意味着生成一个异常名称,而不是生成一个例外数据包?
是的,我想是的。尽管定义通常不单独使用"异常"一词,但其他来源通常将异常名称简称为"异常"—包括在生成它们的特定上下文中。例如,从http://mlton.org/GenerativeException:
在标准ML中,异常声明被称为生成性,因为每次评估异常声明时,都会产生一个新的异常。
(正如您在那里看到的,该页面始终将异常名称称为"异常"。)
标准ML基础库同样以这种方式使用"异常"。例如,从第29页开始:
在一个极端,程序员可以在任何地方使用标准异常
General.Fail
,让它携带一个描述特定故障的字符串。[…]例如,一种技术是让结构Sample
中的函数sampleFn
引发异常Fail "Sample.sampleFn"
。
正如你所看到的,本段使用了两次术语"异常",一次是指异常名称,一次则是指异常值,这取决于上下文来明确其含义。
因此,定义使用短语"generate an exception"来指代生成异常名称是非常合理的(尽管如此,这可能是一个小错误;Definition通常比这更精确、更正式,通常表明它打算何时依赖上下文来消除歧义)。