What makes C code hard to read? It's hard to tell the business logic from the exception handling. There's a lot of error recovery code mixed in with the actual function's purpose.
Exception: Unexpected or abnormal condition arising during program execution.
Python iterators use exceptions to signal the end of iteration, which Zeh says is a weird choice.
- Exception handler bound to code block. - Exception raised in protected block replaces remaining code in block with code in exception handler. - If there is no matching handler, the current function exits and the handler is looked for in the calling subroutine. - Some older languages deviate from this: → Clu: it was possible to attach exception handlers to a function but not a code block. e.g. in java, the try block can be a just a small section of code in a wider scheme. whereas in APL, the only way you can deal with exceptions is to attach a handler to the function itself. → Question: is there a way to simulate this behaviour of fine-tuning where to use exception handlers (like in java) in a language like Clu? →
→ extract x,y,z into its own method that throws an exception, and attach a handler to it from there.
Use of exception handlers: - Perform operations necessary to recover from the exception: do something to restore our program back to some same state and continue computing from the recovered state. - In other situations there may be no recovering, like if a file doesn't exist. We can't really continue without it. Program should terminate gracefully with an appropriate error message.
Represening exceptions: - built-in exception type. - object derived from exception class - any kind of data can be raised as an exception. - The question is: can i just throw any object in the class defined as an exception? depends on the language. In java, you can only throw things that are subclasses of Exception. C++ lets you throw anything. Haskell lets you use monads to implement exception handling in the code, and you can use monads to throw anything.
Raising exceptions: - Automatically by the runtime system as a result of an abnormal condition → eg division by zero generates a system interrupt, which gets translated into an exception by the system. - throw/raise statemtnt to raise exceptions manually - lets say we have a linkedlist in java and we try to take the head of an empty list. this should throw an exception. you might define an EmptyListException, and manually throw it when its attempted to call head on an empty list.
Where can exceptions be handled? - most langs let them be handled locally e.g. some block of protected code with a handler attached. - CLU not APL doesnt allow exceptions to be handled locally. - Some languages (e.g java for some exception types) require exceptions thrown but not handled inside a subroutine to be declared as part of the subroutine definition. → e.g checked/unchecked exceptions. If you declare that a method might throw some known exception, anything that calls it has to deal with that exception, or decide to also declare it as checked and make the caller of THAT deal with it. → Zeh says checked and unchecked exceptions were a poor design decision and the distiction is arbitrary lol. he says look up discussion posts
Handling exceptions without language support - “invent” a value that can be used instead of a real value normally returned by a subroutine. → the problem is when we invent a value, its somehow distinquishable form a normally returned value, and htat's not alwauys the case. e.g. a funciton that returns a number, but when it fails returns 0. the caller doesnt really know whether this was generated normally or due to exception. - return an explicit status value to the caller. → the caller needs to check this status. - rely on the caller to pass a closure to be called in case of an exception → [foo] -→ calls [bar] → [handler] → foo passes handler to bar which knows how to deal with all the exceptions that could happen. telling bar how to recover from errors FOR us, instead of waiting for its return and hten recovering if its wrong.
Exception propagation - exception handlers examined in order. The first one that matches is invoked. → if we have a sequence of exception handlers attached to a given try block, java will go through them and pick thefirst one that matches the tpye of exception that was thrown. - whenever an exception is propagated up a big chain, the stack must be unwound to return back to the state it was before the exception occurred. the cleanup is usually performed with something like a finally block to close all the files and yadda yadda - The caller only knows that a certain method might throw a certain exception. So if it opens a file for this function and the exception happens, closing that file is its responsibility. - A simple implementation: → maintain a stack of exception handlers. whenever we enter a try block, the associated handler gets pushed on to the stack. when we exit the try block, its popped. we immediately know where the handler is to handle the exception thrown by the current subroutine. → every subroutine pushes a special exception handler onot the stack that is executed when control escapes subroutine and performs necessary cleanup. if none of the handlers catch it, this section will destroy the stack frame and send that to the caller → every protected block pushges its exception handler ontothe stack. → exception handlers with multiple alternatives implemented using if then else or switch statements in handler. e.g. pattern matching for several exceptions, as one big exception handler. → Is this a good implementation or not? ⇒ It works, the relevant handler is always on the top. when an exception occurs, pop the top one, if it catches it, im happy, if not, pop the next one and try that, cleaning up the stack and propagating it upwards. ⇒ Your exception hierarchy is essentially a class hierarchy. you need to be able to catch any level of exception ⇒ the exception handler stack is a stack of function pointers basically, telling you where the handler implementation is. ⇒ the issue: you have to push and pop something every single time, for something that is supposed to be an exception (eg not happen often) we want there to be zero additional cost when there is no exceptions, because we want the common case to be super fast, and if the exceptional case is slower then thats okay. ⇒ this is costly because it requires that we constantly maintain this stack of handlers with push and pop operations. - A faster implementation: → store global table mapping mempry addreses of code blocks to exception handlers (generated by compiler) → if something goes wrong in this range of machine code instructions..... go to this handler. in that range... go to this other handler. → When encountering an exception, find exception handler for current instruction using binary search in table. ⇒ search our table for the interval containing that address. ⇒ this is more complicated because youre no longer just using whatevers on the top of the stack, you haave to actually search the loopup table for your curent address. complexity goes from constant to logn. ⇒ this a is a good pprice to pay because the cost of NOT handlig and exception is zero. → cannot be used if program consists of separately compiled units and linker is not aware of this exception handling mechanism. if every unit has its own lookup table, merging them would be complicated. but if you have a reasonable linker, it should probably be able to handle that. -