值多态和"生成异常"

时间:2015-02-14 03:18:34

标签: exception language-lawyer sml value-restriction

Per 标准ML的定义(修订版)

  

这个想法是对非扩展表达式的动态评估既不会生成异常也不会扩展内存的域,而对扩展表达式的评估可能会。

[§4.7,p19;强调我的]

我在网上找到了很多关于ref-cell部分的信息,但几乎没有关于异常部分的信息。 (一些消息来源指出,多态绑定仍有可能引发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

那么,任何人都可以告诉我定义的内容是什么,以及它为什么会提到异常?

(“生成异常”是否可能意味着生成异常名称,而不是生成异常数据包?)

2 个答案:

答案 0 :(得分:2)

我不是一个理论家或正式的语义学家,但我想我理解这个定义试图从运作的角度来看是什么。

ML生成的异常意味着,只要流控制到达相同的异常声明两次,就会创建两个不同的异常。这些不同的对象不仅在内存中,而且在扩展上也是不相等的:我们可以通过模式匹配来区分这些对象与异常构造函数。

[顺便说一句,这显示了大多数其他语言中ML异常和异常之间的重要区别。在ML中,可以在运行时创建新的异常。]

另一方面,如果您的程序两次构建相同的整数列表,则内存中可能有两个不同的对象,但您的程序无法区分它们。它们在延伸上是相同的。

作为生成异常为何有用的一个例子,请考虑MLton的universal type示例实现:

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.
 *)

答案 1 :(得分:2)

[提及Eduardo León的答案是说明定义确实是指这个,并引入短语“生成异常”。我已经对他的回答进行了评价,但我是单独发布的,因为我觉得他的回答来自错误的方向,有些问题:大多数答案都是对已经预先假定的事物的阐述。]

  

“生成异常”是否可能意味着生成异常名称,而不是生成异常数据包

是的,我想是的。虽然 Definition 通常不单独使用“exception”一词,但其他来源通常将异常名称简称为“例外” - 包括在生成它们的特定上下文中。例如,来自http://mlton.org/GenerativeException

  

在标准ML中,异常声明被称为生成,因为每次评估异常声明时,都会产生新的异常。

(正如你在那里看到的那样,该页面始终将异常名称称为“例外”。)

标准ML Basis Library 同样以这种方式使用“exception”。例如,从第29页开始:

  

在一个极端,程序员可以在任何地方使用标准异常General.Fail,让它携带描述特定故障的字符串。 [...]例如,一种技术是在结构sampleFn中使用函数Sample引发异常Fail "Sample.sampleFn"

正如您所看到的,本段使用术语“异常”两次,一次引用异常名称,一次引用异常值,依赖于上下文使其含义清晰。

因此 Definition 使用短语“生成异常”来引用生成异常名称是非常合理的(尽管如此,它可能是一个小错误; 定义通常比这更精确和正式,并且通常表明它何时打算依赖上下文来消除歧义。)