[MLton-user] Improved Exn.withEscape

Stephen Weeks sweeks at sweeks.com
Mon Aug 28 14:58:07 PDT 2006


> A problem with the above design is that the type of withEscape should really
> be (with a well-known extension to the type system)
> 
>   ((Forall 'b. 'a -> 'b) -> 'a) -> 'a

Being able to return any type is one way to express that a function
doesn't return.  One way to do this within the SML type system is to
use a "void" type that has no values.  For example:

  structure Void:>
     sig
        type t
     end =
     struct
        type t = unit
     end
  
  val 'a withEscape: (('a -> Void.t) -> 'a) -> 'a =
     fn f =>
     let
        exception E of 'a
     in
        f (fn x => raise E x) handle E x => x
     end

It is certainly easier for the eye to pick out that the passed escape
function doesn't return when withEscape has the void type.  Compare:

  val withEscape: (('a -> Void.t) -> 'a) -> 'a
  val withEscape: (('a -> 'b) -> 'a) -> 'a

With the void type, in order to use the escape function, it is
essential to have a way to convert the void type to any other type.
Here's a way.

  val dead: 'a -> 'b = fn _ => raise Fail "dead"

With "dead" (the mnemonic is "dead code"), uses of withEscape look
like

  withEscape (fn e => ... dead (e x) ... dead (e y) ...)

or, if there are a lot of uses one can name (and eta wrap) dead o e.

  withEscape (fn e => 
              let 
                 val e = fn ? => (dead o e) ?
              in
                 ... e x ... e y ...
              end)

There isn't a significant difference in client complexity between the
Exit.withLabel and the withEscape approaches.  Compare the above to:

  Exit.withLabel (fn l =>
                  let
                     val e = fn ? => Exit.exitTo l ?
                  in
                     ... e x ... e y ...
                  end)

I think that the void withEscape expresses in the type system the
properties of what's going on more clearly than the Exit.withLabel
version.  And, it doesn't require a specialized structure with a new
type and a couple of values to do it.  One does need the Void
structure and dead function, but those are generally useful for
expressing the concept of a function not returning -- it seems better
to use that general concept in lots of places than to create a new
structure for each place.

So, my assessment is that the right way to go is the void withEscape.



More information about the MLton-user mailing list