Condition system in Ioke


Continuing in the series of “standing on the shoulders of giants”, I will in this post talk a little bit about one feature I always miss in “modern” languages. A condition system. According to Wikipedia, Smalltalk also had one, but I’m only familiar with the Common Lisp and Dylan versions. And that’s where my inspiration is coming from.

So what is a condition system, you might ask? Isn’t that just conditionals? Nope, not really. It’s actually totally different. Conditions are a generalization of errors. This means that errors are a kind of conditions, but you can also do other things with conditions. The main difference is that invoking a condition doesn’t necessarily mean that the stack will be unwinded. Instead, the code handling the condition can choose to do different things depending on the condition. Conditions can have different severity and different default actions. So most real errors would probably end up unwinding the stack if no handler was defined for it. But warnings can also be conditions – and the default warning action might depend on a command line flag. Or maybe you want to have a handler for a specific warning that should be an error in your code. Etc. The possibilities are quite literally endless, and when you can define your own conditions, the power of this mechanism should be apparent. (And if you’re thinking continuations, that’s not exactly it, but almost.)

Common Lisp allow you to disregard some exceptions. You can also restart the code involved that threw an exception, if you think it’s likely to be intermittent. And the handler for this doesn’t need to be lexically close to the place that caused the problem. Instead, the place that raised a condition will give you a couple of options on what to do, commonly called restarts. Restarts are lexical closures that can change the environment that caused the exception, which means it’s possible to fix things quite neatly. Most (all?) Common Lisp implementations have a default exception handler that drops you into the interactive debugger, which is invoked with the lexical closure of the point where the exception happened. You might want to read that last sentence a few times over if it didn’t make sense the first time.

You might ask yourself if this magic is some kind of Lisp trick? That’s a good question, since it seems to mostly be available in Lisp systems, such as Common Lisp and Dylan. (Smalltalk is obviously very Lispy too. =)

There is nothing technical standing in the way for other languages to adopt a condition system. Ruby would do well with it for many things, although I don’t think it would be a great thing to graft it on at this point.

I’m pretty sure I can implement it easily in Ioke, on top of the JVM. The interesting point will be to see how I can make it interact with Java exceptions…

Since I haven’t implemented it yet, I don’t know the exact syntax, but I do have some ideas.

bindHandler(
  noSuchCell: block(c, c asText println),
  incorrectArity: block(c, c asText println),
  # code that might cause the above conditions
)

bindHandler(
  somethingHappened: block(c, invokeRestart(useNewValue, 24))
  loop(
    value = 1
    bindRestart(
      useNewValue: block(val, value = val),
      quit: block(break),

      value println
      signal(somethingHappened)))

These examples show a small subset of what you will be able to do – define handlers for specific conditions, and in these handlers call restarts. I will probably provide some way to collect all handlers for a condition, so Common Lisp style interactive choices can be made. I will try to keep the system a bit easier than the Common Lisp version, but hopefully I’ll be able to retain it’s power.


5 Comments, Comment or Ping

  1. Greg M

    Let me get this straight:

    1) The callee knows there is a problem.
    2) The callee doesn’t know how to deal with it, so throws to the caller.
    3) The caller needs to deal with it within the callee’s context.

    Why must it be dealt with within the callee’s context but not by the callee? Sounds like a horrible layering violation. If there’s some information missing in the callee’s context, the caller should have passed it in – even if that information is complex enough that it must be passed as a closure.

    This sounds so unlikely I suspect I’m missing something major – the only part that definitely made sense to me was the one followed by “You might want to read that last sentence a few times over if it didn’t make sense the first time”! I can see the use for this in an interactive debugging environment, but nowhere else as yet.

    October 14th, 2008

  2. Well. The story is rather: The callee knows there is a problem, the callee have several options on how to handle that problem (where one option is a default), and the caller can change which option should be used, including executing code at the point of options.

    It’s rather a way of decoupling the error and the possible ways of handling it, from setting a strategy how to actually handle it.

    And yes, the last part is true, it’s mostly useful in an interactive environment – but don’t discount it for that reason. A REPL is one of the most useful tools there is when interacting with a live system. Many Lisp systems can be configured to bring up a debugger even when running live, so that a programmer can take a look in the running system, at the point where the problem happened, instead of poking around in back traces. The way you work with a REPL is quite different, and not even Ruby with IRb comes close to how Lisp is generally developed nowadays.

    Of course, restarts can do other things that make more sense in an automated environment. The point is that it’s all open to customization.

    October 14th, 2008

  3. I wonder if I understand this correctly:

    I have a third-party library that expects a certain configuration file to be present. In case where there is no such file it creates a condition.

    Now since that file should be customized on per instance basis it shouldn’t be placed under version control, but rather some example file instead.

    When using library I can add a handler that will detect missing file condition in library. Then it can actually create a config file using the example provided, put some notice to user and try to continue execution (after opening the file and putting it in proper variable in calee binding).

    If that is how it will work than it means a completely new level of clean monkey patching, without having to copy half of the original method just to add this simple behavior. In that case I love it :)

    October 14th, 2008

  4. And another thing: conditions can be easily used to generalize returns. Return-to-caller can be such a condition and default handler should propagate the returned value up. However when defining the block where you use the return-to-coller (in caller :) ) you could define handler that stops propagation and continues execution using returned value.

    Or even better: defining a block by default places such a handler in place while defining method/function doesn’t.

    This would nicely fix the ‘return’ duality of Ruby Procs/lambdas.

    October 14th, 2008

  5. Chris Hughes

    I implemented conditions in python, here: http://code.google.com/p/pyconditions/
    I had a hard time finding anyone who had heard of them :) Maybe you can take some ideas, and have a look at some of the gotchas that were prevalent. The page I reference in the About thing (http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html) I found to be a good resource.

    Regards,
    Chris

    November 10th, 2008

Reply to “Condition system in Ioke”