[clean-list] Clean in the Real World

Fergus Henderson fjh@cs.mu.oz.au
Mon, 15 Dec 2003 11:34:48 +1100


On 14-Dec-2003, Marco Kesseler <m.wittebrood@mailbox.kun.nl> wrote:
> 
> Having read a part of the paper of Simon Peyton Jones et al. about 
> imprecise exceptions, I must say that this is not exactly what I had 
> in mind myself.
> 
> A main issue in the paper seems to be - if you implement exceptions 
> via stack trimming - that "getException (a + b)" may not have the 
> same value as "getException (b + a)", if both a and b lead to an 
> exceptional - but different - value. It depends on which one gets 
> evaluated first, and often the compiler decides things for you.
> 
> The writers end up putting getExeption in the IO Monad, and then 
> suddenly it _is_ allowed to non-deterministically choose one 
> exception value, supposedly without hurting referential transparancy. 
> If all the interesting stuff must end up in the IO Monad I'd rather 
> just use a real imperative language (sorry if I hurt anybodies 
> feelings, but some Monad tricks are getting on my nerves).

It's not really necessary to put getException in the IO Monad.  The IO
Monad provides both nondeterminism and I/O, and here we only need
nondeterminism.  So another alternative would be to have a separate
monad for nondeterminism, and to put getException in that monad instead.
(See <http://www.dcs.gla.ac.uk/mail-www/haskell/msg00560.html> for some
more details.)

The Haskell implementors decided not to do that, because they considered
it simpler to just use the IO Monad.  But they way that they did it is
not the only possibility.

> And it is needless too. All it takes is a cheap syntactical change: 
> do NOT pass the guarded expression as an argument to a function that 
> checks for exceptions. Instead of getException(a + b) write:
> 
> f a b
>     = a + b
>     catch e = ...
> 
> g a b
>     = a + b
>     catch e = ...

I do not agree that this solves the problem.  It breaks fundamental
properties of a language based on lambda calculus.

> Now, catching the exception value has become decoupled from the (a + 
> b) expression. Instead, it becomes associated with f and g.  And "f a 
> b" does _not_ need to have the same value as "g a b", because f and g 
> are different functions.

A major drawback of this scheme is that function definitions can no
longer be seen as equivalent to variable bindings.

In Haskell, the semantics of a function definition

	f a b = a + b

is defined by the translation into "Core Haskell" as

	f = \ a b -> a + b

as is appropriate for a language based on lambda calculus.
But with your approach, there is no way that such a translation could
be used for functions defined with a catch clause.  You could extend
lambda expressions with catch clauses, but there would be no way of
defining a syntax/semantics for lambda expressions with catch clauses
that did not violate referential transparency.

> The only thing that the compiler needs to guarantee is that f a b 
> always delivers the same exceptional value when applied to the same 
> arguments. So, the expression (a + b) must always be evaluated in the 
> same way within a single program. As far as I know this is normally 
> so in non-concurrent evaluation schemes for "synchronous" exceptions.

No, that is not the case.  Normal implementations may often inline
or specialize functions such as `f' or `g' above, and may then apply
different optimizations to the different copies of the function due
to their use in different contexts.  This may then result in different
exceptions being thrown.

-- 
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.