4 ms·
Sure. The type-error example shown here is easily fixable by languages which utilize static typing and therefore make invalid code uncompilable, but it has the
by phoe-krk 6y ago
Sure. The type-error example shown here is easily fixable by languages which utilize static typing and therefore make invalid code uncompilable, but it has the advantage of being easily understood by almost all programmers.
A more contrived example would be a situation in which some piece of data (e.g. a worker's monthly timesheet report) is passed between modules of a programming system, but the receiving module, upon performing validation, discovers e.g. that the employee was working during a holiday.
Handling that situation is hard, since there are multiple ways of handling it, each of them valid in its own specific context. If the employee is in another country where that day is not a holiday, we should proceed without any other actions. If the employee has an agreement with their manager that they are having crunch time, then the system should proceed after applying overtime payment. If the employee is on a flexible time schedule, we should proceed and log this somewhere else; if the employee has no justification for that overtime, we should abort and signal an error; more examples follow.
In other words, when we signal the condition, we do not have full information about what should happen to it. We have the question "What should we do with this timesheet?" and the answer to that question is "It depends."
It is possible to model this situation by a condition type named e.g. EMPLOYEE-WORKING-DURING-HOLIDAY, and instances of that condition type being signaled inside the programming module. We create a dynamic environment where the proper handler routines for EMPLOYEE-WORKING-DURING-HOLIDAY are established, and we call the module's validator function inside that environment.
This process fully decouples the act of signaling a condition from the act of choosing whether to handle that situation and also from the act of choosing how to handle that situation.
One can (and should) document the condition type in the design specifications, and also describe functions that are allowed to signal it.
- pierrebai 6y agoI'm afraid to sound like a negative type, but to me this example is simply a callback? The described EMPLOYEE-WORKING-DURING-HOLIDAY is akin to a system has a slot to register a callback (or signal which can be connected to receiver, to use a Qt-like design) which can handle the situation or decide raise an exception. Again, I understand that it might be attractive to have a single unified system to handle the different possible situations instead of separate systems. I find it hard to imagine a case where the decision to be an exception-like, callback-like or restart-like is chosen dynamically at run-time by any or all participants. And like I said, I' d be afraid that if such a case come up, it would make understanding the design harder, not simpler. For example, as much as Qt signal/slot mechanism is powerful and flexible, I've found that when used fully, it makes understanding the code hard because it becomes impossible to know what will happen when a signal is raised because the handling is so well uncoupled.
- phoe-krk 6y agoYes, you are correct! (And you don't sound negative just yet; skeptical, if anything.) The mechanism of handlers is literally a mechanism of dynamically provided callbacks. I draw the parallel between condition handlers and callbacks/hooks in the body of the book, too. The difference is the fact that Common Lisp has facilities that allow two things: choices of what and how to proceed and flexible non-local returns. The callback is allowed to list all recovery choices that are present in a given dynamic environment; they are established dynamically, just like handlers, and therefore can be provided fully from outside. For instance, a handler/callback can invoke a choice named CONTINUE if it decides that absolutely nothing needs to be done and the validation is safe to proceed. The validating code doesn't need to know why exactly that choice was taken. Or, if it notices that e.g. it needs to convert a timesheet from version 3.0 to version 4.0, it can call a conversion function on the timesheet object and invoke a choice named REVALIDATE, passing the converted timesheet as an argument. Note that the validating code doesn't need to know about any details of the conversion routine! It only needs to provide a means of restarting the validation by establishing a REVALIDATE choice. Or, if it decides that the situation is hopeless, it can signal an error of its own and defer the responsibility of handling that situation higher up - all the way to the system debugger, if necessary. The validating code doesn't need to know about any details of why an error was signaled! That's a ton of modularity that we've just given there. These choices are actualy named restarts in Common Lisp. I purposefully name them choices, though, as I introduce them in the book.
- db48x 6y agoThey are callbacks, but when they return they don't return to you. Instead they return back up the stack to whoever called the function that signalled the condition.
- phoe-krk 6y ago> Instead they return back up the stack to whoever called the function that signalled the condition. Not necessarily. They may return to any point on the stack that has been "announced" as a valid return point. One can compute a list of these return points, choose which one to utilize, and under which conditions. That's what constitutes the power of restarts (and non-local exits) in Common Lisp.
- db48x 6y agoAnother disconnect people often have is that you can actually wait for user input before deciding which restart to use. You can't quite stash the condition in a database and wait until everyone returns from the holiday to resume progress, but that's merely a technical limitation of the normal lisp compilers that are currently available.
- phoe-krk 6y ago> You can't quite stash the condition in a database and wait until everyone returns from the holiday to resume progress, but that's merely a technical limitation of the normal lisp compilers that are currently available. I think that in a multithreaded environment you can try to hang a particular thread by forcing it into the debugger in order to preserve all stack information inside it, but I don't know if that is feasible in the long run on production systems since it opens a gate to possibly DoSing the machine.
- db48x 6y agoOr just have it wait for an RPC message. A thread waiting on IO doesn't cost much, and you can always spin up more to do the work it was supposed to be doing. But yea, conditions weren't designed from the start with distributed systems in mind, so you almost need a runtime that can save a whole thread to disk and recover it later. Back in the day though you could just open a window and ask the user what to do directly.
- phoe-krk 6y ago> and you can always spin up more I think it depends on your OS. IIRC, on Windows, it was fairly easy to destabilize the OS by spawning threads in a loop and starving other processes on the system. I haven't used Windows in a long while though, so I cannot say for sure.
- lonjil 6y agoMaybe that would be feasible with lighter weight green threads. I do this with threads but my system is just a chat bot, so it doesn't really ever turn into a problem.