Alex Jacobson:
> Ooops, I forgot to remove the "and". Anyway, my point is that
> 1. it is not logically consistent to treat exceptions as return values
> 2. as an implementation matter it violates laziness to do so
OK, now I follow. And diagree. ;-) On your second point first:
I'm not sure what you mean by "violates laziness"; it would be true
to say that adding exceptional return values in a given way might
well reduce the laziness of the program; but this can always be
obviated, given sufficient care.
> > Using error-monad syntax might be a bit more palatable, but amounts to
> > essentially the same thing. Alternatively, you can define HOFs to
> > "lift" an n-ary function to an exception-propagating equivalent:
> That is what I did with my Exception version 2 syntax.
> The problem is that doing this lifting ends up being non-lazy.
You're right, it alters the strictness of the program. But one can
recover the original behaviour by delaying/eliminating the pattern-match,
though I again I agree this is a pain.
> You could argue that this problem is an artifact of the Haskell syntax and
> that we could add Exceptions to the Thunk to achieve the desired result
> (treating exceptions as return values).
I don't think I would, though! (If I understand what you mean by this.)
The "problem" is an artifact of wanting to keep the language referentially
transparent, which a built-in throw/catch scheme of the sort you
suggest would scupper.
> Our intuition is that foo' is commutative. foo' a b = foo' b a.
> But that turns out not to be true when you have exceptions.
That's true. And it remains true _however_ one treats exceptions.
There's no way around that in general, I'm afraid, and I'll cite you
assorted papers on Observable Sequentiality if you really want the
grubby details.
> But the real point here is that Exceptions are, by definition, results
> that are outside the domain of the function being evaluated. Treating
> exceptions as algebraic types may be comforting, but what you are really
> doing in that case is extending the domain of your function
Effectively, yes. From a domain theory PoV, this is all that one could
possibly ever do, in fact ('error' included).
> -- and there are limits to how far you can go with that.
These being?
> Truly exceptional conditions are those that truly are outside of the
> domain of the function being evaluated. e.g. factorial -1
> The VALUE of (factorial -1) is not an exception. Neither is the value of
> (factorial (1 `div` 0)).
> When a function is passed bad arguments, it is not meaningful (from a
> functional perspective) to have it return a value.
> The value of a function over arguments outside its domain is undefined.
> When such an event occurs, the logically consistent behavior is to exit
> function evaluation and tell the caller what was wrong with the
> arguments passed (to the extent it is possible to do).
I don't find this argument at all compelling. If a value is "truly
outside the domain of the function being evaluated", then don't pass
it to it! This may seem glib, but I do believe that its better SE
practice in general. If there are exceptional conditions in "the world",
or if determining a sufficient precondition is not practicable, then I
repeat my advice concerning exceptional return values, a necessary evil
though they might be.
> Right now that means using the error function. I am just saying that
> error isn't really enough for a production quality language.
Agreed.
> Does this make more sense?
It makes perfect sense, but I think that having exceptions as a language
mechanism in Haskell is not realistic or viable, for the reasons I
outlined before. I don't pretend that the alternatives are trivial,
or even necessarily very pleasant-looking -- just that they're necessary.
Slainte,
Alex.