Iterators Lecture 24: Iterators & Exceptions CSC 131 Fall, 2014 Kim Bruce Abstract over control structures (in Clu) - where for c : char in string_chars(s) do string_chars = iter (s : string) yields (char); index : Int := 1; limit : Int := string$size (s); while index <= limit do yield (string$fetch(s, index)); index := index + 1; end string_chars; Implementing Iterators Just another object w/state in o-o language What about procedural? How can we retain state? When Good Programs Go Bad Specific kind of coroutine.
Handling Errors Exceptions What happens when something goes wrong, e.g., with read from file. In C returns error condition, which is generally ignored. In more modern languages, throw exception, which must be handled or crash. Designed to handle unexpected errors. Exception handlers based on dynamic calls, not static scope. Allows program to recover from exceptional conditions, esp. beyond programmers control Can be abused Example Exceptions Exception Handling Arithmetic, array bounds, or I/O faults, Failure of preconditions Unpredictable conditions Tracing program flow in debugger Ada: - raise exception_name; - handling: begin C exception when excp_name1 => C' when excp_name2 => C'' when others => C' Java, C++ similar w/ throw & try-catch
Handling Exceptions After Handling... When throw exception -- where look for handler? - Same unit? (Ada/C++/Java) - Calling unit? (Clu) - If not find, continue up call chain (Ada/Java/ML/Haskell): Return from block PL/I: Resumption model: re-execute failed statement. Eiffel: Re-execute block where failure occurred ML & Java -- exceptions can take parameters Haskell uses Monads data Exn a = Oops String Answer a deriving (Show) instance Monad Exn where return a = Answer a -- return :: a -> Exn a -- (>>=) :: M a -> (a -> M b) -> M b (Oops s) >>= f = Oops s (Answer a) >>= f = f a throw :: String -> Exn a throw = Oops catch :: Exn a -> (String -> Exn a) -> Exn a catch (Oops l) h = h l catch (Answer r) _ = Answer r See Stone s ExcInterp.hs for interpreter handling exceptions Exceptions in Java Objects from subclass of Exception class try { Pattern matching } catch (ExcType ex) { } catch (ExcType ex) {...} If not caught, must declare. E.g. public E pop() throws EmptyStackException {... throw new EmptyStackException(); }
RuntimeException If Exception Not Handled If exceptions subclasses of RuntimeException then need not be declared in method headers Ex.: - NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, NumberFormatException, and ArithmeticException Unfortunately, also includes EmptyStackException Pop off activation records while searching for handler. What if allocated memory in unit being popped? OK if garbage collection, but Closing files also problems Talk later about problems Java try-catch-finally So far... try { } catch (ExcType ex) { } catch (Exc'Type ex) { } finally {... } No matter how you complete block, will execute finally clause Structured Programming - Goto considered harmful Exceptions - Structured jumps -- can carry a value - dynamic scoping of exception handler Continuations...
Continuations Continuation of expression is remaining work to be done after evaluating expression - the future - Represented as a function, applied to value of exp, which is value computed so far. Capture continuation - use it later to return to execution. Explicitly represented in Scheme, ML Have been important in compilers for functional languages, concurrency, web programming Example 0: Roots of Quadratic exception notquadratic; exception imaginaryroots; fun equal(x:real, y:real) = x <= y andalso x >= y; fun roots(a, b, c) = let val discbase = (b*b) - (4.0*a*c) val denom = 2.0*a if equal(denom, 0.0) then raise notquadratic else if discbase < 0.0 then raise imaginaryroots else let val disc = Math.sqrt(discBase) if disc > 0.0 then [(~b + disc)/denom, (~b - disc)/denom] else [~b/denom] Example 1: Roots of Quadratic fun checkquad (x, n_continuation, e_continuation) = if equal(x, 0.0) then e_continuation() else n_continuation(x); fun roots1(a, b, c) = let fun econt () = raise notquadratic fun ncont (x) = let val discbase = (b*b) - (4.0*a*c) if discbase < 0.0 then raise imaginaryroots else let val disc = Math.sqrt(discBase) if disc>0.0 then [(~b + disc)/x, (~b - disc)/x] else [~b/x] checkquad(2.0*a, ncont, econt) Example 2: Roots of Quadratic fun checkimaginary (x, n_continuation, e_continuation) = if x < 0.0 then e_continuation() else n_continuation(math.sqrt(x)); fun roots2(a, b, c) = let fun econt () = raise notquadratic fun ncont (x) = let fun econt () = raise imaginaryroots fun ncont (disc) = if disc > 0.0 then [(~b + disc)/x, (~b - disc)/x] else [~b/x] checkimaginary(b*b - 4.0*a*c, ncont, econt) checkquad(2.0*a, ncont, econt)
Example 3: Roots of Quadratic fun checknumroots(disc, continuation1, continuation2) = if disc > 0.0 then continuation1(disc) else continuation2(); fun roots3(a, b, c) = let fun econt () = raise notquadratic fun ncont (x) = let fun econt () = raise imaginaryroots fun ncont (disc) = let fun con1 (disc) = [(~b + disc)/x, (~b - disc)/x] fun con2 () = [~b/x] checknumroots(disc, con1, con2) checkimaginary(b*b - 4.0*a*c, ncont, econt) checkquad(2.0*a, ncont, econt) Final version is tail recursive Continuation-Passing Form Continuation-passing form of fcn f of n values is function w/one more -- continuation f cpf (x 1,...,x n,k) = def k(f(x 1,...,x n )) So f cpf (x 1,...,x n,λy.y) = λy.y(f(x 1,...,x n )) = f(x 1,...,x n ) Use idea to derive cpf form Using Continuations Used w/multiple threads w/separate stack - Blocked thread is represented as ptr to continuation CPS transform allows to rewrite programs so no need to ever return Changing Execution Order Useful in web programming where no state.