Haskell - UMBC CSEE

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