我不是类型理论家或形式语义学家,但我想我理解这个定义试图从操作的角度得到什么。
生成机器学习异常意味着,每当流控制两次到达相同的异常声明时,就会创建两个不同的异常。这些不同的对象不仅在内存中,而且这些对象在扩展上也是不相等的:我们可以通过与异常构造函数进行模式匹配来区分这些对象。
[顺便说一下,这显示了 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.
*)