[clean-list] Clean in the Real World

Marco Kesseler m.wittebrood@mailbox.kun.nl
Tue, 16 Dec 2003 21:04:13 +0100


> 
>> You will:
>> (a) be able to apply equational reasoning within your program, in the 
>> sense that if an exception occurs within foo, it will always deliver 
>> the same result (baz), because it will always throw the same 
>> exception, by virtue of the compiler generating a fixed evaluation 
>> order for bar. If no exception occurs, everything will be as usual 
>> and bar gets returned (but also see below).
>> (b) have compiler optimisations both in bar and baz, as it does not 
>> matter which exception gets thrown in bar, as long as it is the same 
>> one each time within a single program.
>
>I think you've missed the point here.  Yes, you can still use equational
>reasoning in your system, but there are now fewer equations which are
>valid.  For example, previously I had the equation
>
>	a + b  =  b + a
>
>but now this doesn't apply.  If you don't put the exception catcher in a
>nondeterminism monad (or similar), then your language ends up with fewer
>equational properties.  This is precisely what we went to great lengths
>to avoid in Haskell.

Commutativity is NOT a property of all functions. '-' is not 
commutative. '+' is not commutative because it uses the '+' symbol. 
Commutativity can only be established after inspecting the function 
definition.

Say that '+' has been defined like:

(+) a b = add a b.

'add' is a primitive operation that is strict in its arguments. It 
first evaluates arg 1, then evaluates arg 2, and then adds them. All 
of these steps may throw exceptions. Kind of a real world thing (for 
all of you who wonder about this thread's subject).

'+' remains commutative here: no exception thrown inside (a + b) will 
_ever_ influence the value of (a + b), as all exceptions will just 
pass up to higher levels. These higher levels will _not_ be able to 
establish the value of (a + b). They only know that some exception 
was thrown while evaluating it.

Now suppose that we add a general catch to the '+' definition. Just 
for the sake of the argument, because this is probably pointless for 
a function like '+'.

(+) a b
    = add a b
    catch _ = NotANumber /* map any exception to this special value */

Now, '+' is _still_ commutative, because any exception maps to the 
same value. Either everything goes well, and the value of (a + b) 
gets computed, or something goes wrong, and NotANumber gets returned. 
This happens for (a + b) and (b + a) alike.

We take the definition of (+) one step further:

(+) a b
    = add a b
    catch DivideByZero = NotANumber /* the only exception that we 
handle */
    catch e = throw e /* we pass all other exceptions to higher 
levels */

We have now entered the twilight zone. In most cases, '+' will behave 
the same as in the case were it has no handlers at all. Only if a 
division by zero gets thrown first it will deliver NotANumber. This 
means that for some a and b, (a + b) may deliver NotAnumber, while (b 
+ a) may not deliver any value at all, because another exception got 
thrown first. Is '+' still commutative? I don't know. For the values 
I can check it is, but for the others? Let's assume it is not, 
because I am in a good mood.

So, let's REALLY break commutativity!

(+) a b
    = add a b
    catch DivideByZero = NotANumber
    catch _ = AVeryBigValue /* Sad */

Okay, so now we have defined a non-commutative '+'. Do we need 
exceptions for that? No. If I want, I can define a non-commutative 
'+' in Clean today. How about:

(+) a b = a - b.

I may well have missed the point, but I somehow doubt that this was it.

regards,
Marco


>_______________________________________________
>clean-list mailing list
>clean-list@cs.kun.nl
>http://www.cs.kun.nl/mailman/listinfo/clean-list