Error handling strategies

Some kind of language support for error handling (e.g exceptions of various kinds, on error blocks, Maybe types, continuations etc.) has become standard. The exact mechanism is yet another language design decision designers have to make.

Eric Lippert describes VBScript's error handling mechanims. The VBScript approach is perhaps more confusing than it has to be (though I personally didn't find Eric's examples confusing). Tying exception handlers to blocks is more structured and perhaps better.

Be that as it may, I think better error handling constructs are still waiting to be discovered (or designed).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

The URL is broken

It points to LtU...

Fixed URL

Sorry.

Language support required?

In my experience working on Slate's condition-handling, the only primitive support needed to improve on Common Lisp and Dylan's excellent condition-handling systems were easy lexical closures and ensure: (unwind-protect) methods. We have object-oriented conditions with behaviors, and restarts as objects, potentially as local to certain kinds of conditions for polymorphic overrides and so forth. As with Lisp and Dylan, the condition handlers have control before the stack is unwound, and as with Smalltalk, a meta-context can be escaped into where live debugging and editing can occur.

The Erlang capability to propagate errors outside of processes and let the code die to restart is not beyond the scope of what we're doing, although we haven't gotten to that kind of support yet. One capability that Lisp and Dylan don't provide and certainly dogs Smalltalk is the ability to globally handle certain conditions in pre-defined ways, which limit their ability to take advantage of the "let it die" philosophy in an unforgiving production environment. With an increasingly flexible and open system, you can actually start to think about answers to these problems.

Slate is a deliberately simple language with few primitive concepts, so it doesn't take much to support any of this. In fact, I would argue that deliberately removing conventional limits on a language is what made this possible.

Failure as a first-class concept

It is interesting for languages to treat failure as a first-class language concept, requiring explicit handling of any failure conditions that may occur. Java's (controversial) exception declarations provide one approach to this. Icon provides another which is very expressive but quite different.

I'm not a fan of Icon's multi-valued semantics (which wrap a nested list comprehension around the entire program!) But I like the basic style it encourages:

if(f:OpenFile("Log.txt"))
   Write(f,"Some Text")
else
   Print("Failed to open file")

Whereas, on the other hand, writing:

f:=OpenFile("Log.txt")
Write(f,"Some Text")

Is disallowed by the compiler because the "OpenFile" expression may fail, but isn't explicitly handled.

This technique can be combined with dependency and sequencing to allow some very expressive programming interesting idioms, such as:

if(c:ParseChar(),c>='A',c

Which not only tests some conditions, but also creates variable bindings. For example, you can use this for type-aware casting to work in an environment with compile-time array bounds checking:

PrintElement(a:[]int,b:int):=
   if(i:nat 

In this example, we're casting the integer "b" to "the type of natural numbers less than the length of the array a", and binding a variable to the result which can then be safely used to index into the array.

A final example uses "?t" syntax (similar to Haskell "Maybe t") for the type of an optional element of type t, and "^t" for "Pointers to non-null elements of type t":

List:=type{x:int,next:^List}
Display(a:List):void
   Print("Value is ",a.x)
   if(n:a.next)
      Display(n^)

This provides a clean type-theoretic solution to earlier discussion on "nullable pointers", by orthogonalizing the concepts of pointers and optional values, and assuring that in any context where an optional value is used, it must be explicitly tested with a conditional to guarantee that no runtime failure can occur.

I have implemented this on a fairly large scale in an experimental language and feel the results are promising. I'd love to hear feedback on this approach, and also whether readers find this C-Pascal-Python hybrid syntax attractive or revulsive.

Significant Whitespace

I may be the only one on this, but I've never been able to accept significant whitespace. It's been sufficient to keep me from exploring Python to any significant degree (although I should add that I did explore it enough to find that I don't care for several aspects of its semantics, either).

As we've already discussed, I'm very excited about your language's semantics, and I think that significant whitespace aside, the C/Pascal syntax will make it very approachable. And if significant whitespace it's gonna be, well, I'll just have to find a way to make my peace with that. :-)

Failure in Icon is not equivalent to exceptions

I'm assuming you're referring to the Ralph Griswold Icon programming language... At which point I'm not sure your description is entirely accurate. Your file example uses functions which aren't built into Icon. If one uses the correct functions, the following code does compile fine under Icon:

f:=open("Log.txt", "w")
write(f,"Some Text")

I feel your characterization of the failure concept in Icon as being something akin to exceptions is really rather misleading. Failure in Icon is not meant to indicate exceptional conditions; it is really meant to represent the natural termination of a generator [it does have various other uses, but they could all be argued to flow from this requirement].

It's worth noting that Icon doesn't really posess the concept of exception as do more modern programming languages (I presume Unicon does include such a concept). It includes a crude exit function, but that's about it - there's no way to declare errors, and no way to catch them once exit has been called.

I also have very little idea why you would claim that 'Icon's multi-valued semantics ... wrap a nested list comprehension around the entire program'. I can vaguely see that you think that Icon's goal-directed evaluation is a bit like list comprehensions, but I'm not at all sure that one could reasonably say it's wrapped around the entire program. If nothing else, that ignores the significant effect that many expressions in Icon are bound: that is, backtracking will not break out of the bound expression. Since every logical line in an Icon program is implicitly bound in this way, at best you could perhaps argue that each bound expression in Icon is a bit like a list comprehension. But it's still a bit of a stretch IMHO.

Your language looks to be interesting but, for better or worse, it doesn't really appear to be overly similar to Icon.

Language Confusion

Laurence Tratt: I'm assuming you're referring to the Ralph Griswold Icon programming language... At which point I'm not sure your description is entirely accurate. Your file example uses functions which aren't built into Icon. If one uses the correct functions, the following code does compile fine under Icon...

Obviously, Tim can answer this himself, but I want to point out that the code examples Tim shows are in his new language, not Icon, and the specific example referred to here was intended precisely to show how his language's semantics differ from Icon's.

Laurence: Your language looks to be interesting but, for better or worse, it doesn't really appear to be overly similar to Icon.

I think that was part of Tim's point: Icon gives us a suggestive look at a language whose conditionals aren't based on booleans; his language's conditionals are also not based on booleans (in fact, his language doesn't even have a "boolean type" per se). However, his language is deliberately not an Icon clone or derivative.

Of course, this is just my interpretation of his post. He could contradict me. :-)

Re: Language Confusion

Paul: I want to point out that the code examples Tim shows are in his new language, not Icon, and the specific example referred to here was intended precisely to show how his language's semantics differ from Icon's.

That's absolutely fair enough [although Tim might be best advised to be very clear about when he is talking about Icon and when he is talking about his own language], but I do think that Tim's characterization of Icon's 'basic style' is misleading. Just because Icon doesn't have booleans doesn't mean that any language which lacks booleans is in the Icon style. In fact, Icon's lack of booleans is a mere by-product of the fact that one can encode most of the visible effects of boolean logic via Icon's conjunction & and disjunction | operators (the latter of which is a special kind of generator). It's not really a fundamental design issue that Icon doesn't have booleans - they're just not particularly neccessary given the other features which Icon does have.

However as soon as you starts equating failure with exceptions (which is what Tim appears to do, handling exceptions in the conditional of the if in a special way), you've moved a looooooong way from the fundamentals that really define Icon. A really, really long way IMHO. It will be interesting to see if this approach is useful (I'll admit, it seems Draconian to me - but I'd be happy to proved wrong), but most important is not to get confused over what Icon presents to the world.

Paul: Of course, this is just my interpretation of his post. He could contradict me. :-)

Until you said that, I was going to consider your comments binding ;)

Wheat's Errors

In the Wheat project we are exploring a totally different direction: Errors are fundamental objects that self-propagate if operated on (a bit like floating point non-signaling NaNs.) You can read more about them in the Wheat Wiki.

This is similar to error objects in some functional languages (such as Erlang.), but with a richer facility for storing information in the error object. References to other language experiences with such objects would be appreciated.

Wheat

Wheat's approach sounds very useful and implementable. I evaluate expressions that may contain failure by (conceptually) evaluating them as if they were comprehensions in something like Haskell's "Maybe" monad. It would be straightforward to combine this with error codes by evaluating them in a order-dependent monad that looks something like "Perhaps t = Really t | Error string".

I have a similar operator syntax "a||b" which evaluates to a if a doesn't fail, and evaluates to b otherwise. This ends up working almost exactly like C's || operator (including evaluation order for imperative terms), but it propagates the original terms (rather than just a boolean true/false value). Because of the way the failure propagates, a||b is literally translated to "if(x:=a)x else b".

Using these failure semantics, it so happens that no explicit boolean operators are needed: the array-former (a,b) acts as a boolean conjunction, this (a||b) operates as a disjunction, and "!a" negation translates to "if(x:=a) fail else {}".

The operators support variable bindings, so an expression of the form "if(x:=a||b,y:=c).." executes the ".." part if ((a doesn't fail or b doesn't fail) and c doesn't fail), sequences any imperative effects there, and lets you access "x" and "y" with knowledge of their type. For example, if a and b evaluate to integers (or fail), you can use x as if it's an integer.

Continued

Eric returns to the discussion of VBScript error handling.

Part III

Is here. If nothing else, read the first sentence...