5

Per The Definition of Standard ML (Revised):

The idea is that dynamic evaluation of a non-expansive expression will neither generate an exception nor extend the domain of the memory, while the evaluation of an expansive expression might.

[§4.7, p19; emphasis mine]

I've found a lot of information online about the ref-cell part, but almost none about the exception part. (A few sources point out that it's still possible for a polymorphic binding to raise Bind, and that this inconsistency can have type-theoretic and/or implementation consequences, but I'm not sure whether that's related.)

I've been able to come up with one exception-related unsoundness that, if I'm not mistaken, is prevented only by the value restriction; but that unsoundness does not depend on raising an exception:

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

So, can anyone tell me what the Definition is getting at, and why it mentions exceptions?

(Is it possible that "generate an exception" means generating an exception name, rather than generating an exception packet?)

ruakh
  • 175,680
  • 26
  • 273
  • 307

2 Answers2

2

I'm not a type theorist or formal semanticist, but I think I understand what the definition is trying to get at from an operational point of view.

ML exceptions being generative means that, whenever the control of flow reaches the same exception declaration twice, two different exceptions are created. Not only are these distinct objects in memory, but these objects are also extensionally unequal: we can distinguish these objects by pattern-matching against exceptions constructors.

[Incidentally, this shows an important difference between ML exceptions and exceptions in most other languages. In ML, new exception classes can be created at runtime.]

On the other hand, if your program builds the same list of integers twice, you may have two different objects in memory, but your program has no way to distinguish between them. They are extensionally equal.


As an example of why generative exceptions are useful, consider MLton's sample implementation of a 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

This code would cause a huge type safety hole if ML had no value restriction:

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.
 *)
ruakh
  • 175,680
  • 26
  • 273
  • 307
isekaijin
  • 19,076
  • 18
  • 85
  • 153
  • Hi, thanks for your reply. It seems like this answer is just a long-hand way of replying "yes" to the question in my last paragraph? – ruakh Jul 12 '15 at 18:34
  • (Because unless I'm really missing something, you are using the term "exception", or in one place "exception class", to mean what the *Definition* refers to as an "exception name".) – ruakh Jul 12 '15 at 18:35
  • Go ahead, make the change. – isekaijin Jul 12 '15 at 18:46
2

[Hat-tip to Eduardo León's answer for stating that the Definition is indeed referring to this, and for bringing in the phrase "generative exceptions". I've upvoted his answer, but am posting this separately, because I felt that his answer came at the question from the wrong direction, somewhat: most of that answer is an exposition of things that are already presupposed by the question.]


Is it possible that "generate an exception" means generating an exception name, rather than generating an exception packet?

Yes, I think so. Although the Definition doesn't usually use the word "exception" alone, other sources do commonly refer to exception names as simply "exceptions" — including in the specific context of generating them. For example, from http://mlton.org/GenerativeException:

In Standard ML, exception declarations are said to be generative, because each time an exception declaration is evaluated, it yields a new exception.

(And as you can see there, that page consistently refers to exception names as "exceptions".)

The Standard ML Basis Library, likewise, uses "exception" in this way. For example, from page 29:

At one extreme, a programmer could employ the standard exception General.Fail everywhere, letting it carry a string describing the particular failure. […] For example, one technique is to have a function sampleFn in a structure Sample raise the exception Fail "Sample.sampleFn".

As you can see, this paragraph uses the term "exception" twice, once in reference to an exception name, and once in reference to an exception value, relying on context to make the meaning clear.

So it's quite reasonable for the Definition to use the phrase "generate an exception" to refer to generating an exception name (though even so, it is probably a small mistake; the Definition is usually more precise and formal than this, and usually indicates when it intends to rely on context for disambiguation).

Community
  • 1
  • 1
ruakh
  • 175,680
  • 26
  • 273
  • 307