Haskell Kevin Atkinson John Simmons 1 Contents • • • • • • • Introduction Type System Variables Functions Module System I/O Conclusions 2 History of Haskell • Named for Haskell Curry • Created by committee from FPCA conference, 1987 – Needed a “standard” non-strict, purely functional language • Latest version: Haskell 98 3 Goals of the Haskell design • Suitability for teaching, research, and applications • Complete description via the publication of a formal syntax and semantics • Free availability • Basis of ideas that enjoy a wide consensus • Reduction of unnecessary diversity in functional programming languages 4 Interpreters and Compilers • • • • GHC nhc98 HBC / HBI Hugs • www.haskell.org/implementations.html 5 Distinguishing features • Lazy evaluation • Stateless • No exception system - errors are considered equivalent to _|_ 6 Variants and extensions • • • • Parallel variants Object support Pattern guards Views 7 The Haskell type system • value :: Type • Type inference • Pre-defined types – Integer, Real, String, etc. <= case sensitive! – Not special • Polymorphic types • User-defined types 8 User-defined types • Keywords type and data • Enumerated types, renamed types, tuples (record types), lists • Type classes – ex. Equality types – Inheritance ex. Equality => Ordered • Kinds of types - verify type constructors – * means a value type 9 Thunks and ! • Lazy evaluation: the computation needed to compute a field value is stored until needed in a thunk • ! Will force immediate evaluation – Can help constrain types – Use with caution 10 Variables • No keywords to define • Lists (constructor is : ) • Arrays – must open the Array module • Infinite data structures 11 Basic Stuff • Due to layout rules Haskell syntax is rather elegant and generally easy to understand • Functions similar to ML • Pattern and Wildcards like ML – but also more powerful Pattern Gards – Case clause to pattern match inside of functions • Let like ML to define bindings – but also where as part of function and case clause syntax to declare binding after the expression 12 More on Lists • [ f x | x <- xs, x < 10 ] – Reads "the list of all f x such that x is drawn from xs where x < 10" – x <- xs is a generator, x < 10 is a guard • More than one generator can be specifed: [ (x,y) | x <- xs, y <- ys ] – Which forms the cartesian product of the two lists xs and ys • [1 .. 10] => [1,2,3,4,5,6,7,8,9,10] • [1,3 .. 10] => [1, 3, 5, 7, 9] • ints = [1, 2 ..] – take 10 ints => [1,2,3,4,5,6,7,8,9,10] – squares = map (^2) ints – take 3 squares => [1, 3, 9] • numsFrom n = n : numsFrom (n+1) • ones = 1 : ones 13 Fibonacci • fib = 1 : 1 : [ a+b | (a,b) <- zip fib (tail fib) ] – Where zip returns the pairwise interleaving of its two list arguments: zip (x:xs) (y:ys) = (x,y) : zip xs ys zip xs ys = [] – An infinite list, defined in terms of itself, as if it were "chasing its tail." Lazy Evaluation • No Fixed Order of Evaluation • let f x y = a where a = 20 * b b = x + 5 * c c = y * y • As you already see infinite lists are not possible • Proving program correctness is nothing more than a mathematical proof. Not so in ML because one still has to worry about order of evaluation • Many things that were inefficient with strict evaluation or now possible in Haskell. – Can perform complicated operations by function composition. • And Finally … No side effects or State. 15 Standard Prelude • • • • • • • . $ $! flip curry/uncurry id const until head/tail ++ length concat reverse zip zipWith map fold* filter take/drop iterate repeat cycle span any/all elem sum/product maximum/minimum lines words 16 Using The Standard Prelude • wordCount = length . words • fact n = product (take n [1, 2 ..]) • infixl 9 .| (.|) = flip (.) • doubleSpace = lines .| map (++ “\n\n”) .| concat • summary = words .| take 20 .| unwords • wordlist = words .| filter (\x -> length x > 3) .| map (to lower) .| group .| filter (\x length x >= 3) .| map head .| map (++ “\n”) .| concat 17 Error Conditions • Often handled using the Maybe type defined in the Standard Prelude – data Maybe a = Just a | Nothing • doQuery :: Query -> DB -> Maybe Record • r :: Maybe Record r = case doQuery db q1 of Nothing -> Nothing Just r1 -> case do Query db (q2 r1) of Nothing -> Nothing Just r2 -> case doQuery db (q3 r2) of Nothing -> Nothing Just r3 -> ... • Which can get quite tedious after a while • There has to be a better way… 18 Monads • And there is... • thenMB :: Maybe a -> (a -> Maybe b) -> Maybe b mB `thenMB` f = case mB of Nothing -> Nothing Just a -> f a returnMB a = Just a • r = doQuery q1 db `thenMB` \r1 -> doQuery (q2 r1) db `thenMB` \r2 -> doQuery (q3 r2) db `thenMB` \r3 -> returnMB r3 • And what we have is the mathematical notion of Monad which is formally defined as: return a `then` f === f a m `then` return === m m `then` (\a -> f a `then` h) === (m `then` f) `then` h 19 Haskell Monads • In Haskell a Monad is a class defined something like: • class Monad m where >>= :: m a -> (a -> m b) -> m b return :: a -> m a • So for our Database example we will have something like • instance Monad Maybe where (>>=) = thenMB return = returnMB • doQuery q1 db >>= \r1 -> doQuery (q2 r1) db >>= \r2 -> doQuery (q3 r2) db >>= \r3 -> return r3 • Which can be rewritten using Haskell’s do syntax • do r1 <- doQuery q1 db r2 <- doQuery q2 db r3 <- doQuery q3 db return r3 20 Simulating State • To simulate state when there is none simply pass in the state and return a copy of the new state. • parm -> SomeState -> (res, SomeState) • Functions that do so are known as State Transformers. • We can model this notation in Haskell using a type synonym • type StateT s a = s -> (a, s) • parm -> StateT SomeState res 21 State Example • addRec :: Record -> DB -> (Bool,DB) • addRec :: Record -> StateT DB Bool • newDB :: StateT DB Bool newDB db = let (bool1,db1) = addRec rec1 db (bool2,db2) = addRec rec2 db1 (bool3,db3) = delRec rec3 db2 in (bool1 && bool2 && bool3) • As you can see this could get quite tedious • However, with Monads we can abstract the state passing details away. The details or a bit complicated, but the end result will look something like this: • do boo11 <- addRec rec1 bool2 <- addRec rec2 bool3 <- addRec rec3 return (bool1 && bool2 && bool3) 22 I/O • To Perform I/O A State Transformer Must be used, for example to get a character: getChar :: FileHandle -> StateT World Char • But we must make sure each World is only passed to one function so a Monad is used • But this is open to abuse so an ADT must be used: data IO a = IO (StateT World a) • So putChar is now: getChar :: FileHandle -> IO Char • The end result is that any function which uses I/O must return the IO type. • And the only way IO can be executed is by declaring the function “main :: IO ()”. 23 Finally A Complete Program • import System (getArgs) main :: IO () main = do args <- getArgs case args of [fname] -> do fstr <- readFile fname let nWords = length . words $ fstr nLines = length . lines $ fstr nChars = length fstr putStrLn . unwords $ [ show nLines , show nWords , show nChars , fname] _ -> putStrLn "usage: wc fname" 24 References • Hudak, P. et al (2000) “A Gentle Introduction to Haskell Version 98” http://www.haskell.org/tutorial • Jones, S.P. et al (1999) “The Haskell 98 Report” http://www.haskell.org/onlinereport • Winstanley, Noel (1999) “What the Hell are Monads?” http://www.dcs.gla.ac.uk/~nww/Monad.html 25
© Copyright 2026 Paperzz