1 / 117

A taste of Haskell

A taste of Haskell . Simon Peyton Jones Microsoft Research. The challenge. “We are in the middle of the most important upheaval in computer science for several decades” “We have to re-visit and re-think pretty much everything”

blaze
Télécharger la présentation

A taste of Haskell

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. A taste of Haskell Simon Peyton Jones Microsoft Research

  2. The challenge • “We are in the middle of the most important upheaval in computer science for several decades” • “We have to re-visit and re-think pretty much everything” • “The end of the von Neumann model” .... “Computing must be re-invented” Burton Smith, 18 July 2009 So we need to think afresh...

  3. Backus 1978 Can programming be liberated from the von Neumann style? John Backus Dec 1924 – Mar 2007

  4. One response • Purely functional programming is a radical and elegant attack on the whole enterprise of software construction • Radical, because it forces us to re-think how programming is done: • Give up side effects? • But side effects are how mainstream languages compute! • If you want to program a parallel computer, then a promising starting point is a language in which side effects are the exception, not the rule

  5. What is Haskell? • Haskell is a programming language that is • purely functional • lazy • higher order • strongly typed • general purpose • parallel by default

  6. Why should I care? • Functional programming will make you think differently about programming • Mainstream languages are all about state • Functional programming is all about values • Learning Haskell will make you a better programmer in whatever language you regularly use

  7. Spectrum Pure (no effects) Any effect C, C++, Java, C#, VB Excel, Haskell X := In1 X := X*X X := X + In2*In2 Commands, control flow Expressions, data flow • Do this, then do that • “X” is the name of a cell that has different values at different times • No notion of sequence • “A2” is the name of a (single) value

  8. xmonad • xmonad is an X11 tiling window manager written entirely in Haskell Client Client Screen Events (mouse, kbd, client) X11 Window manager Mouse Window placement Client Client Keyboard

  9. Why I’m using xmonad • Because it’s • A real program • of manageable size • that illustrates many Haskell programming techniques • is open-source software • is being actively developed • by an active community

  10. “Manageable size”

  11. Inside xmonad Configuration data Layout algorithm Events (mouse, kbd, client) X11 FFI State machine Window placement Session state (Window model)

  12. The window stack Export list A ring of windows One has the focus Stack.hs module Stack( Stack, insert, swap, ...) where import Graphics.X11( Window ) type Stack = ...Window... focusNext:: Stack -> Stack -- Move focus to next window focusNext = ... swap :: Stack -> Stack -- Swap focus window with next swap = ... Import things defined elsewhere Define new types Specify type of focusNext Comments

  13. The window stack Stack should not exploit the fact that it’s a stack of windows module Stack( Stack, insert, swap, ...) where type Stack w = ... focusNext :: Stack w -> Stack w -- Move focus to next window focusNext = ... swap :: Stack w -> Stack w -- Swap focus with next swap = ... No import any more A stack of values of type w focusNext works fine for any type w

  14. b a The window stack c d e • A list takes one of two forms: • [], the empty list • (w:ws), a list whose head is w, and tail is ws A ring of windows One has the focus The type “[w]” means “list of w” type Stack w = [w] -- Focus is first element of list, -- rest follow clockwise swap :: Stack w -> Stack w -- Swap topmost pair swap [] = [] swap (w : []) = w : [] swap (w1 : w2 : ws) = w2 : w1 : ws The ring above is represented [c,d,e,...,a,b] Functions are defined by pattern matching • w1:w2:ws means w1 : (w2 : ws)

  15. swap [] = [] swap (w:[]) = w:[] swap (w1:w2:ws) = w2:w1:ws Syntactic sugar swap [] = [] swap [w] = [w] swap (w1:w2:ws) = w2:w1:ws [a,b,c] means a:b:c:[] Equations are matched top-to-bottom swap (w1:w2:ws) = w2:w1:ws swap ws = ws swap ws = casewsof [] -> [] [w] -> [w] (w1:w2:ws) -> w2:w1:ws case expressions

  16. Running Haskell • Download: • ghc: http://haskell.org/ghc • Hugs: http://haskell.org/hugs • Interactive: • ghciStack.hs • hugs Stack.hs • Compiled: • ghc –cStack.hs Demo ghci

  17. Rotating the windows A ring of windows One has the focus focusNext :: Stack w -> Stack w focusNext (w:ws) = ws ++ [w] focusNext [] = [] Pattern matching forces us to think of all cases Type says “this function takes two arguments, of type [a], and returns a result of type [a]” (++) :: [a] -> [a] -> [a] -- List append; e.g. [1,2] ++ [4,5] = [1,2,4,5] Definition in Prelude (implicitly imported)

  18. Recursion Recursive call (++) :: [a] -> [a] -> [a] -- List append; e.g. [1,2] ++ [4,5] = [1,2,4,5] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) Execution model is simple rewriting: [1,2] ++ [4,5] = (1:2:[]) ++ (4:5:[]) = 1 : ((2:[]) ++ (4:5:[])) = 1 : 2 : ([] ++ (4:5:[])) = 1 : 2 : 4 : 5 : []

  19. Rotating backwards A ring of windows One has the focus focusPrev :: Stack w -> Stack w focusPrevws = reverse (focusNext (reverse ws)) Function application by mere juxtaposition reverse :: [a] -> [a] -- e.g. reverse [1,2,3] = [3,2,1] reverse [] = [] reverse (x:xs) = reverse xs ++ [x] Function application binds more tightly than anything else: (reverse xs) ++ [x]

  20. Function composition focusPrev :: Stack w -> Stack w focusPrevws = reverse (focusNext (reverse ws)) can also be written focusPrev :: Stack w -> Stack w focusPrev = reverse . focusNext . reverse focusPrev reverse focusNext reverse Definition of (.) from Prelude (f . g) x = f (g x)

  21. Function composition Functions as arguments (.) :: (b->c) -> (a->b) -> (a->c) (f . g) x = f (g x) f.g c b a f g

  22. Things to notice

  23. Things to notice... No side effects. At all. • A call to swap returns a new stack; the old one is unaffected. • A variable ‘s’ stands for an immutable value, not for a location whose value can change with time. Think spreadsheets! swap :: Stack w -> Stack w prop_swaps = swap (swap s) == s

  24. Things to notice... Purity makes the interface explicit • Takes a stack, and returns a stack; that’s all • Takes a stack; may modify it; may modify other persistent state; may do I/O swap :: Stack w -> Stack w -- Haskell void swap( stack s ) /* C */

  25. Things to notice... Pure functions are easy to test • In an imperative or OO language, you have to • set up the state of the object, and the external state it reads or writes • make the call • inspect the state of the object, and the external state • perhaps copy part of the object or global state, so that you can use it in the postcondition prop_swap :: Stack Int -> Bool prop_swap s = swap (swap s) == s

  26. Things to notice... Types are everywhere • Usual static-typing rant omitted... • In Haskell, types expresshigh-level design, in the same way that UML diagrams do; with the advantage that the type signatures are machine-checked • Types are (almost always) optional: type inference fills them in if you leave them out swap :: Stack w -> Stack w

  27. Improving the design

  28. Improving the design A ring of windows One has the focus • Changing focus moves the windows around: confusing! type Stack w = [w] -- Focus is head of list enumerate:: Stack w -> [w] -- Enumerate the windows in layout order -- They are laid out on the screen in this order enumerate s = s

  29. Improving the design A sequence of windows One has the focus • Want: a fixed layout, still with one window having focus a b c d e f g Data type declaration right left Constructor of the type Represented as MkStk [b,a] [c,d,e,f,g] data Stack w = MkStk [w] [w] -- left and right resp -- Focus is head of ‘right’ list -- Left list is *reversed* -- INVARIANT: if ‘right’ is empty, so is ‘left’

  30. A sequence of windows One has the focus Improving the design • Want: a fixed layout, still with one window having focus a b c d e f g Represented as MkStk [b,a] [c,d,e,f,g] right left data Stack w = MkStk [w] [w] -- left and right resp -- Focus is head of ‘right’ list -- Left list is *reversed* -- INVARIANT: if ‘right’ is empty, so is ‘left’ enumerate :: Stack w -> [w] enumerate (MkStklsrs) = reverse ls ++ rs

  31. Moving focus data Stack w = MkStk [w] [w] -- left and right resp -- Focus is head of ‘right’ focusNext :: Stack w -> Stack w focusNext (MkStkls (r:rs)) = MkStk (r:ls) rs right left Nested pattern matching

  32. Oops.. Warning: Pattern match(es) are non-exhaustive In the case expression: ... Patterns not matched: [] data Stack w = MkStk [w] [w] -- left and right resp -- Focus is head of ‘right’ -- INVARIANT: if ‘right’ is empty, so is ‘left’ focusNext :: Stack w -> Stack w focusNext (MkStkls (r:rs)) = MkStk (r:ls) rs focusNext (MkStkls []) = MkStk [] [] • Pattern matching forces us to confront all the cases • But still not right ls must be empty

  33. ghciStack.hs *Stack> quickCheckprop_focusNP *** Failed! after 1 test and 1 shrink MkStk [] [0] Oops.. data Stack w = MkStk [w] [w] -- left and right resp -- Focus is head of ‘right’ -- INVARIANT: if ‘right’ is empty, so is ‘left’ focusNext :: Stack w -> Stack w focusNext (MkStkls (r:rs)) = MkStk (r:ls) rs focusNext (MkStkls []) = MkStk [] [] focus_NP :: Stack Int -> Bool focus_NP s = focusPrev (focusNext s) == s • Testing properties can rapidly expose bugs Does not obey the INVARIANT; rs might be empty

  34. ghciStack.hs *Stack> quickCheckprop_focusNP +++ OK, passed 100 tests Happy data Stack w = MkStk [w] [w] -- left and right resp -- Focus is head of ‘right’ -- INVARIANT: if ‘right’ is empty, so is ‘left’ focusNext :: Stack w -> Stack w focusNext (MkStkls (r:rs)) = mkStk (r:ls) rs focusNext (MkStkls []) = MkStk [] [] mkStk :: [w] -> [w] -> Stack w -- Smart constructor for MkStk, establishes INVARIANT mkStkls [] = MkStk [] (reverse ls) mkStklsrs = MkStklsrs Efficiency note: reverse costs O(n), but that only happens once every n calls to focusPrev, so amortised cost is O(1).

  35. Standing back:data types

  36. Data types • A new data type has one or more constructors • Each constructor has zero or more arguments Built-in syntactic sugar for lists, but otherwise lists are just another data type data Stack w = MkStk [w] [w] data Bool = False | True data Colour = Red | Green | Blue data Maybe a = Nothing | Just a data [a] = [] | a : [a]

  37. Data types data Stack w = MkStk [w] [w] data Bool = False | True data Colour = Red | Green | Blue data Maybe a = Nothing | Just a • Constructors are used: • as a function to construct values (“right hand side”) • in patterns to deconstruct values (“left hand side”) isRed :: Colour -> Bool isRed Red = True isRed Green = False isRed Blue = False Patterns Values

  38. Data types data Maybe a = Nothing | Just a data Stack w = MkStk [w] [w] -- Invariant for (MkStklsrs) -- rs is empty => ls is empty • Data types are used • to describe data (obviously) • to describe “outcomes” or “control” module Stack( focus, ... ) where focus :: Stack w -> Maybe w -- Returns the focused window of the stack -- or Nothing if the stack is empty focus (MkStk _ []) = Nothing focus (MkStk _ (w:_)) = Just w A bit like an exception... ...but you can’t forget to catch it No “null-pointer dereference” exceptions module Foo where import Stack foos = ...case (focus s) of Nothing -> ...do this in empty case... Just w -> ...do this when there is a focus...

  39. Data type abstraction module Operations( ... ) where import Stack( Stack, focusNext ) f :: Stack w -> Stack w f (MkStk as bs) = ... OK: Stack is imported NOT OK: MkStk is not imported module Stack( Stack, focusNext, focusPrev, ... ) where data Stack w = MkStk [w] [w] focusNext :: Stack w -> Stack w focusNext (MkStklsrs) = ... Stack is exported, but not its constructors; so its representation is hidden

  40. Haskell’s module system module X where import P import Q h = (P.f, Q.f, g) • Module system is merely a name-spacecontrol mechanism • Compilertypically does lots of cross-moduleinlining • Modules can begrouped intopackages module P(f,g) where import Z(f) g = ... module Q(f) where f = ... module Z where f = ...

  41. Doing I/O

  42. Where is the I/O in xmonad? • All this pure stuff is very well, but sooner or later we have to • talk to X11, whose interface is not at all pure • do input/output (other programs) A functional program defines a pure function, with no side effects The whole point of running a program is to have some side effect Tension

  43. Where is the I/O in xmonad? • All this pure stuff is very well, but sooner or later we have to • talk to X11, whose interface is not at all pure • do input/output (other programs) Configuration data Layout algorithm Events (mouse, kbd, client) FFI State machine X11 Window placement Session state

  44. Doing I/O putStr :: String -> () -- Print a string on the console • Idea: • BUT: nowmight do arbitrary stateful things • And what does this do? • What order are the things printed? • Are they printed at all? swap :: Stack w -> Stack w [putStr “yes”, putStr “no”] Order of evaluation! Laziness!

  45. The main idea A value of type (IO t) is an “action” that, when performed, may do some input/output before delivering a result of type t. putStr :: String -> IO () -- Print a string on the console • “Actions” sometimes called “computations” • An action is a first class value • Evaluating an action has no effect; performingthe action has an effect

  46. A helpful picture A value of type (IO t) is an “action” that, when performed, may do some input/output before delivering a result of type t. type IO a = World -> (a, World) -- An approximation result :: a IO a World out World in

  47. Simple I/O () String String getLine putStr getLine :: IO String putStr :: String -> IO () main :: IO () main = putStr “Hello world” Main program is an action of type IO ()

  48. Connecting actions up () String getLine putStr Goal: read a line and then write it back out

  49. Connecting actions up echo :: IO () echo = do{ l <- getLine; putStrl } () String getLine putStr echo We have connected two actions to make a new, bigger action.

More Related