Introduction to Haskell Matt Mullins Texas A&M Computing Society October 6, 2009 Matt Mullins (TACS) Introduction to Haskell October 6, 2009 1 / 39
Outline Introduction to Haskell Functional Programming Haskell Specifics Beginning Haskell Using Haskell First Bits of Syntax Slightly More Advanced Types A Real Haskell Program Core Logic Monads The Parser Bringing it all together Matt Mullins (TACS) Introduction to Haskell October 6, 2009 2 / 39
Outline Introduction to Haskell Introduction to Haskell Functional Programming Haskell Specifics Beginning Haskell Using Haskell First Bits of Syntax Slightly More Advanced Types A Real Haskell Program Core Logic Monads The Parser Bringing it all together Matt Mullins (TACS) Introduction to Haskell October 6, 2009 3 / 39
Introduction to Haskell What is functional programming? Functional Programming Instead of a series of modifications, functional programming uses immutable data. Variables contents never change Matt Mullins (TACS) Introduction to Haskell October 6, 2009 4 / 39
Introduction to Haskell What is functional programming? Functional Programming Instead of a series of modifications, functional programming uses immutable data. Variables contents never change Functions (i.e. pure functions) always return the same result when given the same input. There is no state when using pure functions the only state information is the function arguments. This is a more mathematical use of the word function, but it is still extremely usable for computer programs. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 4 / 39
Introduction to Haskell Functional Programming Why should I use it? Easier to test, prevent bugs, etc Easier to prove correctness Haskell uses lazy evaluation: expressions are evaluated only when necessary, allowing data structures like the infinite list. Estimating theoretical performance can be easy, because you can use the mathematical definitions Can still use non-functional programming constructions more on that later Matt Mullins (TACS) Introduction to Haskell October 6, 2009 5 / 39
Why should I use it? Introduction to Haskell Functional Programming Easier to test, prevent bugs, etc Easier to prove correctness Haskell uses lazy evaluation: expressions are evaluated only when necessary, allowing data structures like the infinite list. Estimating theoretical performance can be easy, because you can use the mathematical definitions Can still use non-functional programming constructions more on that later Can theoretically be automatically made concurrent by the compiler Useful now that dual- and quad-core computers are common Still a subject of research: the functionality is nowhere near complete. It still probably won t help you. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 5 / 39
Why shouldn t I use it? Introduction to Haskell Functional Programming Somewhat difficult to debug, since you can t step through code Lazy evaluation means your code isn t always evaluated in the same order. Can be hard to read someone else s code Estimating real performance can be difficult Some APIs don t have bindings for Haskell yet Matt Mullins (TACS) Introduction to Haskell October 6, 2009 6 / 39
Introduction to Haskell Haskell Specifics What sets Haskell apart (from other functional languages)? Main difference: everything is purely functional. Other functional languages like Scheme and OCaml allow you to modify variables after all. Lazy evaluation is impossible. Don t be scared. You can still do things that seem non-functional. Monads make this possible. Haskell is strongly-typed. Haskell has a well-designed module system, much like many newer languages such as Python and C#. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 7 / 39
Outline Beginning Haskell Introduction to Haskell Functional Programming Haskell Specifics Beginning Haskell Using Haskell First Bits of Syntax Slightly More Advanced Types A Real Haskell Program Core Logic Monads The Parser Bringing it all together Matt Mullins (TACS) Introduction to Haskell October 6, 2009 8 / 39
Running the code Beginning Haskell Using Haskell Haskell code can be compiled or interpreted. Compiler: ghc Glasgow Haskell Compiler Interpreter: ghci or hugs Matt Mullins (TACS) Introduction to Haskell October 6, 2009 9 / 39
Running the code Beginning Haskell Using Haskell Haskell code can be compiled or interpreted. Compiler: ghc Glasgow Haskell Compiler Interpreter: ghci or hugs In this presentation: Lines that begin with Prelude> are the ghci interpreter Everything else would be put into a source code file. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 9 / 39
Beginning Haskell First Bits of Syntax Variables and functions Variable definition uses the = operator. Prelude> let a = 3 Prelude> a 3 The let keyword is used only with the interpreter. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 10 / 39
Beginning Haskell First Bits of Syntax Variables and functions Variable definition uses the = operator. Prelude> let a = 3 Prelude> a 3 The let keyword is used only with the interpreter. Function application is a space, instead of parentheses. Prelude> let f x = x+5 Prelude> f 39 44 Matt Mullins (TACS) Introduction to Haskell October 6, 2009 10 / 39
Beginning Haskell First Bits of Syntax Functions with multiple parameters Very similar construction: Prelude> let g x y z = x+y-z Prelude> g 3 4 5 2 How on earth can this work? Matt Mullins (TACS) Introduction to Haskell October 6, 2009 11 / 39
Beginning Haskell First Bits of Syntax Functions with multiple parameters Very similar construction: Prelude> let g x y z = x+y-z Prelude> g 3 4 5 2 How on earth can this work? Functions are just as much values as anything else (like a number, for instance). Function application is an operator that binds more tightly than any other. It always takes exactly one parameter, and returns another function with one fewer parameter. It is left-associative. That is: f a b c ((f a) b) c Matt Mullins (TACS) Introduction to Haskell October 6, 2009 11 / 39
Beginning Haskell Slightly More Advanced Scope Very important, similar to structured languages like C++. What happens if: Prelude> let a = 1; b = 2 Prelude> let h a b = a+b Prelude> h 5 6 Matt Mullins (TACS) Introduction to Haskell October 6, 2009 12 / 39
Beginning Haskell Slightly More Advanced Scope Very important, similar to structured languages like C++. What happens if: Prelude> let a = 1; b = 2 Prelude> let h a b = a+b Prelude> h 5 6 The answer is 11, not 3. The a and b in the function refer to its arguments, not the ones already defined. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 12 / 39
Beginning Haskell Slightly More Advanced Operators Infix operators are treated simply as functions. For example, (+) is simply a function that takes two numbers and returns the sum. Parentheses are important: they make an infix operator behave like a function name. You can make up your own operators, as long as they use only the following:!#$%&*+./<=>?@\^ -~ Matt Mullins (TACS) Introduction to Haskell October 6, 2009 13 / 39
Beginning Haskell Slightly More Advanced Pattern matching When you define a function, the left side is really a pattern that the compiler matches against. Variable names match anything, and their contents are available on the right side. Constructors and literals must match exactly. Patterns are matched top-to-bottom, until a match succeeds. Example: matching a list: count [] = 0 count (x:xs) = 1 + count xs Matt Mullins (TACS) Introduction to Haskell October 6, 2009 14 / 39
Various operators Beginning Haskell Slightly More Advanced Function composition: (.) :: (a->b) -> (c->a) -> c -> b (f. g) x f (g x) Function application: ($) :: (a->b) -> a -> b Why are these used? Matt Mullins (TACS) Introduction to Haskell October 6, 2009 15 / 39
Beginning Haskell Slightly More Advanced Unnamed functions Two main ways to do it: Lambda expressions: (\x -> x+3) Half-evaluated function (or operators): g 1 2 (3+) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 16 / 39
Beginning Haskell Types Haskell type system Every expression in Haskell has an unambiguous type. You can explore the type of an expression using the :t interpreter command: Prelude> :t a a :: Integer Prelude> :t g g :: (Num a) => a -> a -> a -> a Most types are inferred from context (or syntax) A type doesn t have to be unique. :: is used to express the type of a particular expression. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 17 / 39
Beginning Haskell Types Lists An element cons-ed with the rest of the list The cons operator is :, and the empty list is [], so 1:(2:(3:[])) is a list 1,2,3. : is right-associative, so the above is equivalent to 1:2:3:[]. There is syntactic sugar that does this for you; it is also equivalent to [1,2,3] A list can be as long as you want, but all the elements have to be of the same type. A type specification would look like [1,2,3] :: [Integer] Matt Mullins (TACS) Introduction to Haskell October 6, 2009 18 / 39
Beginning Haskell Types Tuples A tuple is a fixed-length collection of elements that can be treated as a single object. A tuple uses the syntax (1,2), which can be used in more complicated ways, like (1,2,(3, a )) A tuple can have elements of different types, unlike a list. The type specification looks like (1,2,(3, a )) :: (Integer, Integer, (Integer, Char)) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 19 / 39
Defining your own types Beginning Haskell Types data IntList = IntItem Integer IntEnd Defines two constructors : IntItem :: Integer -> IntList and IntEnd :: IntList Types can have parameters like functions: data List a = Item a End This defines two constructors: Item :: a -> List a and End :: List a Note that End is simultaneously of an infinite number of types. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 20 / 39
Defining your own types Beginning Haskell Types data IntList = IntItem Integer IntEnd Defines two constructors : IntItem :: Integer -> IntList and IntEnd :: IntList Types can have parameters like functions: data List a = Item a End This defines two constructors: Item :: a -> List a and End :: List a Note that End is simultaneously of an infinite number of types. You can also define type synonyms: type OtherList = List Char Matt Mullins (TACS) Introduction to Haskell October 6, 2009 20 / 39
Type classes Beginning Haskell Types A type class is a set of types that support the same operations. We ve already seen one type class already: Num. Other important classes are: Show: function show :: (Show a) => a -> String Eq: operator (==) :: (Eq a) => a -> a -> Bool Ord: supports operations to determine ordering (i.e. <, >, etc.) Allows for polymorphism. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 21 / 39
Outline A Real Haskell Program Introduction to Haskell Functional Programming Haskell Specifics Beginning Haskell Using Haskell First Bits of Syntax Slightly More Advanced Types A Real Haskell Program Core Logic Monads The Parser Bringing it all together Matt Mullins (TACS) Introduction to Haskell October 6, 2009 22 / 39
A Real Haskell Program A course scheduler As course registration time nears, I want a program which can determine the schedule that is most ideal for me. My requirements: Large contiguous blocks of time outside of class Accepts input in a format that is easy to type and read Why I chose Haskell: The ability to derive new data types with minimal code Extremely easy to define a parser for a domain-specific language I tried it in C++ first, and the only algorithm I could come up with was recursive much easier to express in Haskell. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 23 / 39
The data file format A Real Haskell Program Something easy to type into a text file; example: CPSC 221{ 501{ MWF 11:30 12:20 HRBB 124; TR 15:55 16:45 HRBB 232; } 502{ MWF 11:30 12:20 HRBB 124; MW 12:40 13:30 HRBB 232; } } ENGL 301{ 570{MWF 13:50 14:40 BLOC 114; } } Matt Mullins (TACS) Introduction to Haskell October 6, 2009 24 / 39
A Real Haskell Program The core data structures Core Logic data Weekday = M T W R F deriving (Eq, Ord, Show) data Period = Period { day :: Weekday, start, end :: Time } deriving (Eq, Ord) data Section = Section { course :: Course, number :: Int, periods :: [Period] } deriving Eq data Course = Course { desig :: String, sections :: [Section] } deriving (Eq, Show) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 25 / 39
A Real Haskell Program Determining overlapping periods Core Logic overlap1 :: Period -> Period -> Bool overlap1 p q = (start p >= start q && start p <= end q) (end p >= start q && end p <= end q) overlap :: Period -> Period -> Bool overlap p q = (day p == day q) && (overlap1 p q overlap1 q p) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 26 / 39
A Real Haskell Program Determining conflicting sections Core Logic conflp :: [Period] -> [Period] -> Bool conflp (x:xs) ys = any (overlap x) ys conflp xs ys conflp [] _ = False conflicts :: Section -> Section -> Bool conflicts a b = conflp (periods a) (periods b) conflict :: [Section] -> Bool conflict (s:ss) = any (conflicts s) ss conflict ss conflict [] = False Matt Mullins (TACS) Introduction to Haskell October 6, 2009 27 / 39
A Real Haskell Program Core Logic Choosing one section from each class choosells :: [[Section]] -> [[Section]] choosells (c:cs) = concat [map (x:) $ choosells cs x <- c] choosells [] = [[]] choosells takes its input as one list element per course, and its output is one list element per possible schedule. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 28 / 39
A Real Haskell Program Core Logic Choosing one section from each class choosells :: [[Section]] -> [[Section]] choosells (c:cs) = concat [map (x:) $ choosells cs x <- c] choosells [] = [[]] choosells takes its input as one list element per course, and its output is one list element per possible schedule. choose :: [Course] -> [[Section]] choose cs = choosells (map sections cs) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 28 / 39
A Real Haskell Program Which schedule is the best? Core Logic score ps = let byday = [filter ((d==). day) ps d <- [M,T,W,R,F]] time [] = 0 time l = fromintegral $ (end $ last l) - (start $ head l) times = map time byday :: [Float] meantime = (sum times) / 5.0 in sum $ map (\x -> (x-meantime)^2) times This is the variance of the length of each day. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 29 / 39
A Real Haskell Program Monads What is a Monad? A monad is some type M that supports the two operations: Promoting an expression: return :: a -> M a Combining monads: (>>=) :: M a -> (a->m b) -> M b. Note that a monadic type must have a single type argument. There is no function (in the general case) to unbox a monad. The way in which the (>>=) operator combines monads is defined by the individual monad. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 30 / 39
A Real Haskell Program Monads What is a Monad? A monad is some type M that supports the two operations: Promoting an expression: return :: a -> M a Combining monads: (>>=) :: M a -> (a->m b) -> M b. Note that a monadic type must have a single type argument. There is no function (in the general case) to unbox a monad. The way in which the (>>=) operator combines monads is defined by the individual monad. Allows for stateful computation in a purely functional world. Began life as an abuse of the Haskell type system, and quickly gained status as one of its most distinctive features. Matt Mullins (TACS) Introduction to Haskell October 6, 2009 30 / 39
The IO Monad A Real Haskell Program Monads The type IO a represents an action whose value is of type a. The combination operator performs the actions in sequence. Useful functions: putstr :: String -> IO () putstrln :: String -> IO () getchar :: IO Char getline :: IO String getcontents :: IO String Matt Mullins (TACS) Introduction to Haskell October 6, 2009 31 / 39
A Real Haskell Program Monads The Parser Monad From the parsec library The type GenParser tok st val represents a parser which turns [tok] into something of type val Usually use a Parser GenParser Char () Combination operator creates a parser that matches in sequence You can break out of a Parser, but only in a purely functional way: parse :: GenParser tok () a -> String -> [tok] -> a Matt Mullins (TACS) Introduction to Haskell October 6, 2009 32 / 39
do syntax A Real Haskell Program Monads do s <- getline putstr "Hello, " putstrln s is syntactic sugar for getline >>= (\s -> putstr "Hello, " >> putstrln s) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 33 / 39
do syntax A Real Haskell Program Monads do s <- getline putstr "Hello, " putstrln s is syntactic sugar for getline >>= (\s -> putstr "Hello, " >> putstrln s) With this syntax, you can almost forget it s purely functional Matt Mullins (TACS) Introduction to Haskell October 6, 2009 33 / 39
The small bits A Real Haskell Program The Parser time = do hour <- many1 digit char : minute <- many1 digit return (t (strtoint hour) (strtoint minute)) period = do date <- many1 $ oneof [ M, T, W, R, F ] spaces start <- time spaces end <- time spaces char ; spaces return [Period (chartoweekday d) start end d <- date] Matt Mullins (TACS) Introduction to Haskell October 6, 2009 34 / 39
Match a section A Real Haskell Program The Parser sectionnumber = many1 (digit) >>= return. stringtoint section = do num <- sectionnumber spaces char { spaces periods <- many1 period spaces char } spaces return (num, concat periods) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 35 / 39
And now a course A Real Haskell Program The Parser coursep = do name <- coursename char { spaces sections <- many1 section spaces char } spaces (let c = Course name s s = [Section c n p (n,p) <- sections] in return c) courses = many1 coursep Matt Mullins (TACS) Introduction to Haskell October 6, 2009 36 / 39
Sorry A Real Haskell Program The Parser Sorry, guys, that huge block of code was boring. My apologies. It was easy to write, though, which is why I used Haskell Matt Mullins (TACS) Introduction to Haskell October 6, 2009 37 / 39
Output function A Real Haskell Program Bringing it all together putsingle :: [Section] -> IO () putsingle [] = putstrln "" putsingle (s:ss) = do putstr (show s) putsingle ss putsched :: [(Score, [Section])] -> IO () putsched [] = putstrln "" putsched ((sc,s):ss) = do putstr (show sc) putstr "\t" putsingle s putsched ss Matt Mullins (TACS) Introduction to Haskell October 6, 2009 38 / 39
The main expression A Real Haskell Program Bringing it all together scoresched :: [Section] -> Int scoresched x = score. sort. concat. map periods $ x dowork :: [Course] -> IO () dowork cs = let sch = schedule cs ss = map (\s -> (scoresched s, s)) sch sed = sortby (comparing fst) ss in putsched sed main = do s <- getcontents (case parse courses "stdin" s of (Right cs) -> dowork cs (Left cs) -> print cs) Matt Mullins (TACS) Introduction to Haskell October 6, 2009 39 / 39