Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Steffen Jost - Haskell is Pure

Clojure Workshops
December 20, 2013
120

Steffen Jost - Haskell is Pure

Clojure Workshops

December 20, 2013
Tweet

Transcript

  1. About myself Dr Steffen Jost [email protected] 2010– “Wissenschaftlicher Mitarbeiter” at

    LMU, chair for theoretical computer science (Prof. Hofmann) 2005–2010 Research Fellow, St Andrews, Scotland 2002–2005 PhD student, LMU Munich, Bavaria 1995–2002 Diploma Mathematics, TU Darmstadt, Hessia Research Focus: Automated Program Analysis Type Systems Formal Methods Functional Programming
  2. Haskell vs. Mathematics Haskell is general-purpose programming language. Haskell is

    not replacement for computer algebra systems. However, math-specific libraries exist, like for any other language. Arbitrary-precision integers built-in Int vs. Integer Arbitrary-precision rational numbers built-in List-comprehension looks like Set-comprehension Syntax often motivated by mathematics Mathematics studies ways of abstract thinking, programming is a form abstract thinking.
  3. Haskell is good for you Haskell may reduces the cost

    of software development: Focus on what to compute, not how Fast development of fast code Concise, yet readable code – makes it easier to maintain and to verify Strongly typed: if it compiles, it works – reduces testing Parallelism is easy I learned Haskell a couple of years ago, having previously programmed in Python and (many) other languages. Recently, I’ve been using Python for a project (. . . ), and find my Python programming style is now heavily influenced (for the better, I hope ;-) by my Haskell programming experience. (Graham Klyne, Haskell-Wiki) However: Many true are for all functional languages!
  4. Problems with Functional Languages / Haskell Completely different way of

    thinking Programmers must relinquish micro-control, hence machine-oriented optimisation becomes difficult Many functional features now available in non-declarative languages GC, HO Fkt., Anonyme Fkt., etc. Strongly typed: lots of errors until compilation succeeds Lack of commercial support (IDE, Debugger,. . . ) Programmers usually reject restrictions, however: “with great power comes great ability to shoot oneself in the foot” Restrictions beneficial for multi-person projects, and functional programming seems to be on the rise again.
  5. What is different about Haskell? Haskell is pure! No assignment

    No side effects Evaluation order does not matter ⇒ equational reasoning suffices Haskell is strongly typed Haskell is white-space sensitive Many tutorial/documentation online available Haskell is named after logician Haskell Curry (1900-82). Haskell is just a standard; different Implementations available. Main implementation: Haskell Platform consisting of Libraries, Interpreter and Compiler (GHC = Glasgow/Glorious Haskell Compiler)
  6. Referential Transparency Value of identifiers (variables) never change One expression

    always evaluates to the same value No side effects! ⇒ enhances modularity, code locally comprehensible Simplifies testing and verification ⇒ equational reasoning Order of evaluation is irrelevant ⇒ optimisation, lazy evaluation, parallelism Persistence & Sharing: Changing a data structure requires copying, but unchanged parts can always be referenced ⇒ efficient Algorithms possible, but different (Okasaki) Many other functional Languages are impure and allow imperative updates z.B. F#, Scala, SML, OCaml, Erlang
  7. Haskell is pure Function definition through equations: double1 x =

    x + x -- Function with 1 Argument foo x y z = x + y * double z -- with 3 Arguments add = \x y -> x + y -- Anonymous Fn. 2 Arguments twice f x = f x x -- Higher-order function double2 y = twice (+) y double3 = twice (+) -- Partial application Function application through white-space Left-associative: f x y z == ((f x) y) z Infix-to-Prefix with Parenthesis: (+) 1 2 == 1 + 2 Prefix-to-Infix with Backticks: add 1 2 == 1 ‘add‘ 2 User-definable infixes,∗ e.g. low-precedence right-associative infix $ defined by ($) f x = f x just saves parenthesis: double1 $ 1 + 2 == double1 (1 + 2) ∗Infixes may not start with letter or digit, infix-constructors must start with :
  8. Function definitions in Haskell foo :: Type1 -> Type2 ->

    Type3 -> ResultType foo var1 var2 var3 = expr1 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  9. Function definitions in Haskell foo :: Type1 -> Type2 ->

    Type3 -> ResultType foo var1 var2 var3 = expr1 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  10. Function definitions in Haskell foo :: Type1 -> Type2 ->

    Type3 -> ResultType foo var1 var2 var3 = expr1 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  11. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo var_1 ... var_n = expr1 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  12. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo var_1 ... var_n = expr1 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  13. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo var_1 ... var_n = expr1 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  14. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo pat_1 ... pat_n = expr1 foo pat21 ... pat2n = expr2 foo pat31 ... pat3n = expr3 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  15. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo pat_1 ... pat_n = expr1 foo pat21 ... pat2n | grd211, ..., grd21i = expr21 | grd221, ..., grd22i = expr22 foo pat31 ... pat3n Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  16. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo pat_1 ... pat_n = expr1 foo pat21 ... pat2n | grd211, ..., grd21i = expr21 | grd221, ..., grd22i = expr22 foo pat31 ... pat3n | grd311, ..., grd31k = expr31 | grd321, ..., grd32l = expr32 Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  17. Function definitions in Haskell foo :: Type1 -> ... ->

    Type3 -> ResultType foo pat_1 ... pat_n = expr1 foo pat21 ... pat2n | grd211, ..., grd21i = expr21 | grd221, ..., grd22i = expr22 foo pat31 ... pat3n | grd311, ..., grd31k = expr31 | grd321, ..., grd32l = expr32 where idA = exprA idB = exprB Typdeclaration (optional, typenames always in upper-case) Functionname (must appear in the same column) Argument names Body is a single expression Case-distinction through pattern matching Refine cases through boolean pattern guards First matching branch is evaluated (top-to-bottom) Local definitions may be written underneath
  18. Haskell Expressions Function application through whitespace: f x Anonymous function

    abstraction: \x -> e Conditional: if b then x else y Pattern-Match: guards useable as on top- level match case e of p1 -> e1 p2 | g1 -> e2-1 | g2 -> e2-2 p3 -> e3 Local definitions: allows function definitions always mutual recursive white-space sensitive: indent-to-right: previous line continues indent-exact: new local definition indent-to-left: let concluded let x = e1 y a b = e2 z = e3 in e4
  19. Examples {- This is a comment. - Anything after --

    is also a comment. -} show_signed :: Int -> String show_signed i | i>=0 = "+" ++ show i -- Concatenation by infix ++ | otherwise = show i printPercent :: Double -> String printPercent x = leadZero ++ (show roundPercent) ++ "%" where roundPercent :: Double roundPercent = (fromIntegral $ round $ 1000.0*x) / 10.0 leadZero = if rx < 10.0 then "0" else ""
  20. Haskell is strongly typed Well-typed programs can’t go wrong! (Milner,

    1978) Never type errors at runtime! Errors are recognised at compile time No runtime type checks allow faster execution Types = Documentation Type names always in upper-case No hassel: GHC almost always infers all types Basic types: Char, Int, Integer, Double, Real, Rational,. . . Functiontypes: Int -> Int, (Int -> Int) -> (Int -> Int),. . . Algebraic Datatypes: Tupels, Enums, Lists, Records,. . .
  21. Function Types double :: Integer -> Integer double x =

    x + x foo :: Int -> Int -> Int -> Int foo x y z = x + y * double z twice :: (Int -> Int -> Int) -> Int -> Int twice f x = f x x Funktionstyps are right-associative foo :: Int -> (Int -> (Int -> Int)) to allow partial application cf. “currying” foo 42 :: Int -> (Int -> Int) foo 42 3 :: Int -> Int
  22. Tuples addpair :: (Int,Int) -> Int addpair (0,y) = y

    addpair (x,y) = x+y swap :: (Int,Char) -> (Char,Int) swap (x,y) = (y,x) snd3 :: (Bool,String,Double) -> (String) snd3 (_,s,_) = s Both type and value written using ( , , , ) 0-tuple () referred to as unit-type () Size of tuples is fixed: A function mapping n-tuple to n + 1-tuple for arbitrary n cannot be written due to the strong typing constraints (template haskell allows this)
  23. Lists reverse :: [Char] -> String -- reverse [1,2,3] ==

    [3,2,1] reverse [] = [] reverse (h:t) = reverse t ++ [h] concat :: [[Int]] -> [Int] -- concat [[1,2],[3,4]] == [1,2,3,4] Lists are written using [ , , , ] “Cons”-operator is infix (:) :: a -> [a] -> [a] Syntactic sugar for lists: [1,2,3,4] -- shorthand for 1:2:3:4:[] [’c’..’g’] -- evaluates to "cdefg" [1,3..10] -- evaluates to [1,3,5,7,9] List comprehension similar notated to math. set-compreh. [ (x,z) | x <- [1,3..5], y <- [0..x], even y, let z = y+1 ] evaluates to [(1,1),(3,1),(3,3),(5,1),(5,3),(5,5)]
  24. Enumerations and Arbitrary Recursive Datatypes data Bool = True |

    False data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun next :: Day -> Day next Mon = Tue next Tue = Wed ... Constructors always start with upper-case Constructors may be defined in infix notation Constructors may take arguments: data Person’ = Man String Int | Woman String Double data IntList = Nil | Cons Int IntList -- recurive
  25. Records data Person = Mann { name:: String, age ::

    Int } | Frau { name:: String, height:: Double} p1 = Mann { name = "Alfred", age = 77 } p2 = Frau "Lagertha" undefined p3 = p2 { height = 1.77 } -- p3 points to name of p2 p3 = let p1 = p1 { age = age p1 + 1} -- no update just shadowing in p1 Pattern matching may be partial: isWoman :: Person -> Bool isWoman Frau {} = True isWoman _ = False Projections are automatically defined: name :: Person -> String alter :: Person -> Int
  26. Polymorphism Function types might carry type parameters, always lower-case: flip

    :: (a -> b -> c) -> b -> a -> c flip f x y = f y x Constructors may be parameterised as well: data Maybe b = Just b | Nothing data List a = Cons a (List a) | Nil head :: [a] -> Maybe a head [] = Nothing head (h:_) = Just h map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = f x : map f xs Type safety is still ensured: entities of unknown type may only be passed on different type parameters may be of different types
  27. Type Classes (Interfaces) Haskell’s response to overloading, dynamic dispatch, etc.

    A type class specifies a set of functions: class Eq a where (==), (/=) :: a -> a -> Bool -- two functions required (/=) x y = not $ x == y -- default implementation Datatypes may implement these interfaces, we say “type x is an instance of class y”: instance Eq Person (Mann n1 a1) == (Mann n2 a2) = n1 == n2 && a1 == a2 (Frau n1 g1) == (Frau n2 g2) = n1 == n2 && g1 == g2 _p1 == _p2 = False Polymorphic parameters may be restricted to one or more classes: groupBy :: (a -> a -> Bool) -> [a] -> [[a]] -- explicit group :: Eq a => [a] -> [[a]] -- overloading groupBy parametric polymorphism: always uses same code group ad-hoc polymorphism: code depends on runtime type The class mechanism still ensures complete type safety!
  28. Type Classes (Interfaces) GHC may automatically infer instance for simple

    classes: data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving (Eq, Ord, Enum, Bounded) data Person’ = Mann’ String Int | Frau’ String Double deriving (Eq, Show) It is also possible to define derived instance definitions: instance (Eq a) => Eq [a] where [] == [] = True (x:xs) == (y:ys) = x == y && xs == ys _xs == _ys = False If we can compare entities of type a, then we also know how to compare lists containing type a
  29. Haskell is lazy Most Haskell implementations are lazily evaluated, i.e.

    any needed expression is evaluated at most once. Speeds up evaluation: map double $ map double $ map double [1..10] iterates only once over the list ignores errors in unused code segments: > let x = map (10/) [10,5,2,0,undefined,4,1] > x!!5 2.5 > x [1.0,2.0,5.0,Infinity,*** Exception: Prelude.undefined Allows dealing with “infinite” data > take 10 [1..] [1,2,3,4,5,6,7,8,9,10] > let x = [1..3] ++ x > take 10 $ x [1,2,3,1,2,3,1,2,3,1]
  30. Haskell is lazy Lazy evaluation allows circular program definitions: fibs

    :: [ Integer ] -- List of all Fibonacci numbers fibs = 1 : 1 : (zipWith (+) fibs (tail fibs )) {- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] -} fib :: Int -> Integer fib n = fibs !! n -- memoisation already included! Allpws seperation of data from control flow: Specify how Fibonacci numbers are calculated Decide how many elsewhere Every Fibonacci calculated at most once per program run Problem: may lead to memory leaks Problem: often cited as hindrance to understand execution
  31. I/O Problem: I/O essentially just side-effects, but Haskell allows no

    side-effects! Solution: Monads Basic Idea: I/O functions receive an argument symbolizing the world state, change this state, and return the modified world state. Implementing such a torch-relay is tedious, hence it is hidden behind syntactic sugar. Benefits: 2 All side-effects are advertised through the type. Only a function showing a particular side-effect (such as I/O) may call other functions having that side-effect. (thanks to strong typing again)
  32. DO Notation Essentially syntactic sugar hiding the torch relay: do

    me1 -- me1 :: M a me2 -- me2 :: M b x <- me3 -- me3 :: M c, x :: c let y = f x -- f :: c -> d, y :: d when b $ me4 y -- b :: Bool, me4 :: d -> M () z <- forM [1..3] me5 -- me5 :: Int -> M e, z :: [e] return z -- :: M [e] Mimics the imperative style, adequate for I/O Evaluation order automatically observed Effects trickle through as expectes Can always be coded without (and sometimes is)
  33. DO Notation DO-Notation’s syntactic sugar revealed: do me1 x <-

    me2 me3 $ f x could be written as me1 >> me2 >>= \x -> me3 (f x) Binary operators defined in Monad-Class: class Monad m where (>>=) :: forall a b. m a -> (a -> m b) -> m b (>>) :: forall a b. m a -> m b -> m b return :: a -> m a fail :: String -> m a instance Monad IO instance Monad Maybe instance Monad [] ⇒ purely functional code! Not an extension!
  34. DO Notation DO-Notation’s syntactic sugar revealed: do me1 x <-

    me2 me3 $ f x could be written as me1 >> me2 >>= \x -> me3 (f x) Binary operators defined in Monad-Class: class Monad m where (>>=) :: forall a b. m a -> (a -> m b) -> m b (>>) :: forall a b. m a -> m b -> m b return :: a -> m a fail :: String -> m a instance Monad IO instance Monad Maybe instance Monad [] ⇒ purely functional code! Not an extension!
  35. I/O example module GoodWorld where import System.IO import System.Time (getClockTime,

    ClockTime(..)) main :: IO () main = do (TOD seconds n) <- System.Time.getClockTime let greeting = goodX seconds System.IO.putStrLn $ greeting ++ " world!" goodX :: Integer -> String goodX s = | isMorning = "Good morning" | otherwise = "Good day" where isMorning = 12 > s ‘mod‘ (60 * 60 * 24) ⇒ Imperative features like loops and conditionals definable within DO-Notation
  36. Example: State Monad Doing the torch-relay with a truly variable

    state import Data.STRef.Lazy import Control.Monad.ST.Lazy state :: [a] -> [a] state l = runST $ do -- runST :: (forall s. ST s a) -> a ref_l <- newSTRef l l1 <- readSTRef ref_l let l1’ = reverse l1 writeSTRef ref_l l1’ state2 ref_l state2 :: STRef s b -> ST s b state2 ref_l = do -- superfluous "do" and "return" l2 <- readSTRef ref_l return l2 Code who reads or writes state is inside the monad Type System prohibits mixing up references
  37. Parallelism and Concurrency No race conditions, if the evaluations order

    is unimportant, which simplifies pure parallelism: Glasgow parallel Haskell (GpH) Parallelism by inserting two semantic-preserving primitives:: par :: a -> b -> b -- evaluate arguments parallel pseq :: a -> b -> b -- force evaluation but sometimes requires manual forcing of thunks Par Monade Even more simple, but monadic Nested Data Parallelism Provides operations for specialised big data structure, for example Arrays using CUDA Software Transactional Memory (STM) Concurrency, explicit forking using IO-monad, communication via special shared variables, backtracking built-in, but unlike above prone to deadlocks
  38. Par Monade do v1 <- new v2 <- new fork

    $ put v1 (f x) fork $ put v2 (g x) get v1 get v2 return (v1 + v2) Forking is explicit Results will always be fully evaluated Results communicated via write-once IVar’s Parallel computation fully deterministic runPar :: Par a -> a -- teuer fork :: Par () -> Par () -- billig new :: Par (IVar a) get :: IVar a -> Par a put :: NFData a => IVar a -> a -> Par () Partitioning must still be decided by programmer
  39. Template Haskell Metaprogramming: Haskell code computes Haskell code ghci -XTemplateHaskell

    :m + Language.Haskell.TH > let x = [| \x -> x+1 |] x :: Q Exp > runQ x LamE [VarP x_0] (InfixE (Just (VarE x_0)) (VarE GHC.Num.+) (Just > $(x) 3 4 Quasi-Quoting: Code within Oxford-brackets [| |] becomes data Splicing: Data representing code becomes code again $( ) Nesting possible Q monad deals with bookkeeping, e.g. fresh identifiers
  40. Template Haskell {-# LANGUAGE TemplateHaskell -#} import Language.Haskell.TH projNI ::

    Int -> Int -> ExpQ -- generic projection I-th element projNI n i = lamE [pat] rhs where pat = tupP (map varP xs) rhs = varE (xs !! (i - 1)) xs = [ mkName $ "x" ++ show j | j <- [1..n] ] $(projNI 4 3) (’a,’b’,’c’,’d’) -- evaluiert zu ’c’ Code of projNI executed at compile time! Usage $(projNI) may only occur in another module.
  41. Template Haskell can also process other languages . . .

    let jSkillCases t = mconcat [jShowiSkill i t | i <- heroTypeList ] let widget = do toWidget [lucius| -- CSS .itemSelector { width: 11em; height: 13em; } |] addScriptRemote "http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js" toWidget [julius| -- JavaScript function Aendern () { var tc = document.getElementById(’#{fvId htypeView}’); switch (tc.value) { ^{jSkillCases} } } document.getElementById(’#{fvId htypeView}’).setAttribute(’onchange’,’Aendern()’); |] forM_ heroTypeList (\i -> toWidget [julius| -- JavaScript function Show#{show i}Skill () { document.getElementById(’heroImage’).setAttribute(’class’,’#{displayHero i}’); document.getElementById(’SkillAtag’).innerHTML = ’#{displayHeroSkill i 1}’; } |]) [whamlet| --HTML <table> <tbody align="right"> <tr> <td> #{fvLabel aliasView} # ^{fvInput aliasView} <td> . . . Yesod Framework
  42. Links and Literature Haskell Tools Haskell Platform haskell.org Standard Libraries

    haskell.org/ghc/docs/latest/html/libraries Haskell Online Tutorials School of Haskell www.fpcomplete.com/school 2013 Learn You a Haskell for Great Good learnyouahaskell.com 2010 by Miran Lipovaˇ ca Real World Haskell book.realworldhaskell.org/read 2008 by Bryan O’Sullivan, Don Stewart, John Goerzen A Gentle Introduction To Haskell www.haskell.org/tutorial 2000 by Paul Hudak, John Peterson, Joseph Fasel (Links clickable)