10/21/08 cs242 � Midterm: Wed. Oct. 22, 7-9pm, Gates B01 � Closed book, but you may bring one, letter-sized page of notes, double sided. � SCPD students: if you are local, please come to campus to take the exam. � Kathleen Fisher � Homework assigned 10/15 will be ungraded, � But we strongly urge you to do it! � Solutions will be passed out on 10/20 � Reading: “Tackling the Awkward Squad,” Sections 1-2 � � “Real World Haskell,” Chapter 7: I/O � Minor corrections to HW3 posted (#’ s 3 and 5). � Reminder: you can work on homework in pairs. � Thanks to Simon Peyton Jones for many of these slides. � Functional programming is beautiful: � Concise and powerful abstractions � But to be useful as well as beautiful , a higher-order functions, algebraic data types, language must manage the “Awkward Squad”: � parametric polymorphism, principled overloading, ... � Input/Output � Close correspondence with mathematics � Imperative update � Semantics of a code function is the math function � Equational reasoning: if x = y, then f x = f y � Error recovery (eg, timing out, catching divide by zero, etc.) � Independence of order-of-evaluation (Church-Rosser) � Foreign-language interfaces � e1 * e2 The compiler can Concurrency � choose the best order in which to do e1’ * e2 e1 * e2’ evaluation, including The whole point of a running a program is to skipping a term if it affect the real world, an “update in place. ” � is not needed. � result Do everything the “usual way”: � In a lazy functional language, like Haskell, the I/O via “functions” with side effects: � order of evaluation is deliberately undefined , so putchar ‘x’ + putchar ‘y’ the “direct approach” will not work. � Imperative operations via assignable reference cells: � Consider: � res = putchar ‘x’ + putchar ‘y’ z = ref 0; z := !z + 1; f(z); Output depends upon the evaluation order of ( + ). � w = !z (* What is the value of w? *) Consider: � ls = [putchar ‘x’, putchar ‘y’] Error recovery via exceptions � Output depends on how the consumer uses the list. Foreign language procedures mapped to “functions” � If only used in length ls , nothing will be printed Concurrency via operating system threads � because length does not evaluate elements of list. � Ok if evaluation order is baked into the language . � 1
10/21/08 The reading uses a web server as an example. � Laziness and side effects are incompatible. � Lots of I/O, need for error recovery, need to Side effects are important! � call external libraries, need for concurrency � For a long time, this tension was embarrassing to the lazy functional programming community. � Client 1 � Client 2 � Client 3 � Client 4 � In early 90’ s, a surprising solution (the monad) emerged from an unlikely source (category theory). � Web server � Haskell’ s IO monad provides a way of tackling 1500 lines of Haskell � 700 connections/sec � the awkward squad: I/O, imperative state, exceptions, foreign functions, & concurrency. � Writing High-Performance Server Applications in Haskell by Simon Marlow � Monadic Input and Output � A functional The whole point of program defines a running a program Tension � pure function, with is to have no side effects. � some side effect. � Streams � Program issues a stream of requests to OS, which Move side effects outside of functional program � responds with a stream of inputs. � If Haskell main :: String -> String Continuations � User supplies continuations to I/O routines to specify Wrapper Program, written in some other language � how to process results. � standard standard World-Passing � input output Haskell � The “World” is passed around and updated, location location main like a normal data structure. � (file or (file or program � Not a serious contender because designers didn’ t know stdin) � stdin) � how to guarantee single-threaded access to the world. � Stream and Continuation models were discovered to be inter-definable. � But what if you need to read more than one file? Or delete files? Or communicate over a socket? ... � Haskell 1.0 Report adopted Stream model. � 2
10/21/08 Move side effects outside of functional program � Enrich argument and return type of main to include all input and output events. � If Haskell main :: [Response] -> [Request] main :: [Response] -> [Request] data Request = ReadFile Filename | WriteFile FileName String | … data Response = RequestFailed Haskell � | ReadOK String program � | WriteOk [ Response ] [ Request ] | Success | … Wrapper program interprets requests and Laziness allows program to generate requests prior to adds responses to input. � processing any responses. � Haskell 1.0 program asks user for filename, echoes name, reads file, and prints to standard out. � Hard to extend: new I/O operations require adding new constructors to Request and main :: [Response] -> [Request] main ~(Success : ~((Str userInput) : ~(Success : ~(r4 : _)))) Response types and modifying the wrapper. � = [ AppendChan stdout "enter filename\n", ReadChan stdin, No close connection between a Request and AppendChan stdout name, ReadFile name, corresponding Response , so easy to get “out- AppendChan stdout of-step,” which can lead to deadlock. � (case r4 of Str contents -> contents Failure ioerr -> "can’t open file") The style is not composable: no easy way to ] where (name : _) = lines userInput combine two “main” programs. � The ~ denotes a lazy pattern , which is evaluated ... and other problems!!! only when the corresponding identifier is needed. � A value of type ( IO t ) is an “action. ” When A value of type ( IO t ) is an “action. ” When performed, it may do some input/output before performed, it may do some input/output before delivering a result of type t . � delivering a result of type t . � type IO t = World -> (t, World) result :: t IO t World in World out 3
10/21/08 Char () A value of type ( IO t ) is an “action. ” When Char performed, it may do some input/output before delivering a result of type t . � getChar putChar type IO t = World -> (t, World) “Actions” are sometimes called “computations. ” � getChar :: IO Char Main program is an action putChar :: Char -> IO () of type IO () � An action is a first-class value. � main :: IO () Evaluating an action has no effect; main = putChar ‘x’ performing the action has the effect. � >>= >>= (>>=) :: IO a -> (a -> IO b) -> IO b To read a character and then write it back out, we need to connect two actions. � () () Char getChar putChar Char getChar putChar We have connected two actions to make a new, bigger action. � echo :: IO () echo = getChar >>= putChar >>= >>= Operator is called bind because it binds the result of the left-hand action in the action on echoDup :: IO () the right. � echoDup = getChar >>= (\c -> putChar c >>= (\() -> Performing compound action a >>= \x->b: � putChar c )) performs action a, to yield value r � applies function \x->b to r � performs the resulting action b{x <- r} � The parentheses are optional because lambda returns the resulting value v � abstractions extend “as far to the right as possible. ” � v The putChar function returns unit, so there is r x a b no interesting value to pass on. � 4
Recommend
More recommend