stack setup
to install a private GHCCreate a file called hello.hs
with the following contents:
main = putStrLn "Hello, world!"
Compile your program to a native executable like this:
$ ghc --make hello
[1 of 1] Compiling Main ( hello.hs, hello.o )
Linking hello ...
$ ./hello
Hello, world!
Or run it in the GHCI interpreter like this:
$ ghci hello.hs
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
...
Ok, modules loaded: Main.
*Main> main
Hello, world!
*Main>
Haskell uses the =
sign to declare bindings:
x = 2 -- Two hyphens introduce a comment
y = 3 -- ...that continues to end of line.
main = let z = x + y -- let introduces local bindings
in print z -- program will print 5
;
”, which is usually auto-inserted by a layout ruleadd arg1 arg2 = arg1 + arg2 -- defines function add
five = add 2 3 -- invokes function add
Parentheses can wrap compound expressions, must do so for arguments
bad = print add 2 3 -- error! (print should have only 1 argument)
main = print (add 2 3) -- ok, calls print with 1 argument, 5
x = 5
x = 6 -- error, cannot re-bind x
safeDiv x y =
let q = div x y -- safe as q never evaluated if y == 0
in if y == 0 then 0 else q
main = print (safeDiv 1 0) -- prints 0
x = 5 -- this x is not used in main
main = let x = x + 1 -- introduces new x, defined in terms of itself
in print x -- program "diverges" (i.e., loops forever)
In C, we use mutable variables to create loops:
long
factorial (int n)
{
long result = 1;
while (n > 1)
result *= n--;
return result;
}
In Haskell, use recursion to “re-bind” argument symbols in new scope
factorial n = if n > 1
then n * factorial (n-1)
else 1
This Haskell code requires n
stack frames
factorial n = if n > 1 then n * factorial (n-1) else 1
factorial n
multiplies by n
after evaluating factorial (n-1)
Idea: use accumulator argument to make calls tail recursive
factorial n = let loop acc n' = if n' > 1
then loop (acc * n') (n' - 1)
else acc
in loop 1 n
loop
is tail recursive, compiles to an actual loopwhere
clausesGuards let you shorten function declarations:
factorial n = let loop acc n' | n' > 1 = loop (acc * n') (n' - 1)
| otherwise = acc
in loop 1 n
|
” symbol introduces a guardTrue
guard winsotherwise = True
Bindings can also end with where
clauses—like inverted let
factorial n = loop 1 n
where loop acc n' | n' > 1 = loop (acc * n') (n' - 1)
| otherwise = acc
let
, a where
clause scopes over multiple guarded definitionsloop
) often have arguments related to outer function
'
(prime) to the inner-function’s argument'
character in variables, except as first characterfactorial n = loop 1 n
where loop acc n' | n' > 1 = loop (acc * n) (n' - 1) -- bug
| otherwise = acc
Easier to type shorter symbol when you mean long than vice versa
So can avoid problem by using longer symbol name for the outer function (i.e., shorter name for shorter scope)
factorial n0 = loop 1 n0
where loop acc n | n > 1 = loop (acc * n) (n - 1)
| otherwise = acc
factorial n0 = loop 1 n
” causes compile errorBool
- either True
or False
Char
- a unicode code point (i.e., a character)Int
- fixed-size integerInteger
- an arbitrary-size integerDouble
- an IEEE double-precision floating-point number->
type2 - a function from type1 to type2(
type1,
type2,
…,
typeN)
- a tuple (where N ≠ 1)()
- a zero-tuple, pronounced unit (kind of like void
in C); there is only one value of this type, also written ()
You can declare the type of a symbol or expression with ::
x :: Integer
x = (1 :: Integer) + (1 :: Integer) :: Integer
::
has lower precedence than any function operators (including +
)Function application happens one argument at a time (a.k.a. “currying”)
add :: Integer -> (Integer -> Integer)
add arg1 arg2 = arg1 + arg2
add 2 3
is equivalent to (add 2) 3
(add 2)
takes 3 returns 5, so (add 2) has type Integer -> Integer
->
associates to the right, so parens usually omitted in multi-argument function types:fn ::
argType1 ->
argType2 ->
… ->
argTypeN ->
resultType:t
*Main> :t add
add :: Integer -> Integer -> Integer
The data
keyword declares user-defined data types (like struct
in C):
data PointT = PointC Double Double deriving Show
PointT
with constructor PointC
containing two Double
sderiving Show
means you can print the type (helpful in GHCI)Read
, Eq
, Ord
, Enum
, Bounded
Types and constructors can use the same name (often do), E.g.:
data Point = Point Double Double deriving Show
One type can have multiple constructors (like a tagged union):
data Point = Cartesian Double Double
| Polar Double Double
deriving Show
data Color = Red | Green | Blue | Violet deriving (Show, Eq, Enum)
Constructors act like functions producing values of their types
data Point = Point Double Double deriving Show
myPoint :: Point
myPoint = Point 1.0 1.0
data Color = Red | Green | Blue | Violet deriving (Show, Eq, Enum)
myColor :: Color
myColor = Red
case
statements & function bindings “de-construct” values with patterns
getX, getMaxCoord :: Point -> Double
getX point = case point of
Point x y -> x
getMaxCoord (Point x y) | x > y = x
| otherwise = y
isRed :: Color -> Bool
isRed Red = True -- Only matches constructor Red
isRed c = False -- Lower-case c just a variable
Given the following types for a rock-paper-scissors game:
data Move = Rock | Paper | Scissors
deriving (Eq, Read, Show, Enum, Bounded)
data Outcome = Lose | Tie | Win deriving (Show, Eq, Ord)
outcome :: Move -> Move -> Outcome
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
...
*Main> outcome Rock Paper
Lose
*Main> outcome Scissors Paper
Win
*Main> outcome Paper Paper
Tie
data Move = Rock | Paper | Scissors deriving (Eq, Read, Show, Enum, Bounded)
data Outcome = Lose | Tie | Win deriving (Show, Eq, Ord)
-- | @outcome our_move their_move@
outcome :: Move -> Move -> Outcome
outcome Rock Scissors = Win
outcome Paper Rock = Win
outcome Scissors Paper = Win
outcome us them | us == them = Tie
| otherwise = Lose
data Maybe a = Just a
| Nothing
data Either a b = Left a
| Right b
You can see these at work in GHCI:
Prelude> :t Just True
Just True :: Maybe Bool
Prelude> :t Left True
Left True :: Either Bool b
Left True
contains a type variable, b
Left True
can be of type Either Bool b
for any type b
_
” can be bound but not used
isJust :: Maybe a -> Bool -- note parametric polymorphism
isJust (Just _) = True
isJust Nothing = False
isRed Red = True
isRed _ = False -- we don't need the non-red value
isZero 0 = True -- can match built-in literals, too
isZero _ = False
_
avoids thisYou can deconstruct types and bind variables within guards, E.g.:
addMaybes mx my | Just x <- mx, Just y <- my = Just (x + y)
addMaybes _ _ = Nothing
though often there is a simpler way
addMaybes (Just x) (Just y) = Just (x + y)
addMaybes _ _ = Nothing
We could define homogeneous lists with the data
keyword
data List a = Cons a (List a) | Nil
oneTwoThree = (Cons 1 (Cons 2 (Cons 3 Nil))) :: List Integer
List Integer
, the type is written [Integer]
Cons
, the constructor is called :
and is infixNil
, the empty list is called []
oneTwoThree = 1:2:3:[] :: [Integer]
oneTwoThree' = [1, 2, 3] -- comma-separated elements within brackets
oneTwoThree'' = [1..3] -- define list by a range
String
is just a list of Char
, so ['a', 'b', 'c'] == "abc"
You can pattern match on literal lists and String
s
head :: [a] -> a
head (x:_) = x
head [] = error "head: empty list"
tail :: [a] -> [a] -- all but first element
tail (_:xs) = xs
tail [] = error "tail: empty list"
(++) :: [a] -> [a] -> [a] -- infix operator concatenates lists
[] ++ ys = ys
(x:xs) ++ ys = x : xs ++ ys
length :: [a] -> Int -- This code is from language spec
length [] = 0 -- GHC implements differently, why?
length (_:l) = 1 + length l
filter :: (a -> Bool) -> [a] -> [a]
filter pred [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
Note function error :: String -> a
reports assertion failures
deriving Read
and reads
deriving Show
” and show
to print values
show
show gives you a valid Haskell expression*Main> show $ Point 1.0 1.0
"Point 1.0 1.0" <-- could paste string into your source
“deriving Read
” lets you parse a value at runtime
data Point = Point Double Double deriving (Show, Read)
reads
parses and returns [(value, string_with_rest_of_input)]
*Main> reads "invalid Point 1 2" :: [(Point, String)]
[]
*Main> reads "Point 1 2" :: [(Point, String)]
[(Point 1.0 2.0,"")]
*Main> reads "Point 1 2 and some extra stuff" :: [(Point, String)]
[(Point 1.0 2.0," and some extra stuff")]
*Main> reads "(Point 1 2)" :: [(Point, String)] -- note parens OK
[(Point 1.0 2.0,"")]
reads
Write a function to parse moves:
parseMove :: String -> Maybe Move
Just
move on successful parse, Nothing
otherwiseExamples of use:
*Main> parseMove "Rock"
Just Rock
*Main> parseMove "Paper"
Just Paper
*Main> parseMove "Scissors plus extra junk"
Nothing
Use reads
:
parseMove :: String -> Maybe Move
parseMove str | [(m, "")] <- reads str = Just m
| otherwise = Nothing
reads
return type implicitly constrained by parseMove
’s type declaration
Removing parseMove
’s type would make calling it difficult
Directly match keywords:
parseMove :: String -> Maybe Move
parseMove "Rock" = Just Rock
parseMove "Paper" = Just Paper
parseMove "Scissors" = Just Scissors
parseMove _ = Nothing
Note how strings are constructors—you can pattern match on them
But this solution too finicky—won’t accept trailing carriage returns or spaces. If you did this change to using reads.
"\n"
or "\r\n"
parseMove :: String -> Maybe Move
parseMove str | [(m, rest)] <- reads str, ok rest = Just m
| otherwise = Nothing
where ok = all (`elem` " \r\n")
*Main> parseMove "Rock \r\n"
Just Rock
*Main> parseMove "Rock \r\njunk"
Nothing
length
function?haskell.org
” is too long for me—I change to “ho
”Foldable
(we’ll talk about classes later)List.length
lenAcc
is tail recursiveFoldable
Here’s a function to count lower-case letters in a String
import Data.Char -- brings function isLower into scope
countLowerCase :: String -> Int
countLowerCase str = length (filter isLower str)
length
tail recursive, countLowerCase
might run in constant space
Recall Haskell evaluates expressions lazily… Means in most contexts values are interchangeable with function pointers (a.k.a. thunks)
A String
is a [Char]
, which is a type with two values, a head and tail
But until each of the head or tail is needed, it can be stored as a function pointer
So length
will cause filter
to produce Char
s one at a time
length
does not hold on to characters once counted; can be garbage-collected at will
Here’s an even more concise definition
countLowerCase :: String -> Int
countLowerCase = length . filter isLower
The “.
” operator provides function composition
(f . g) x = f (g x)
f . g
” is an ASCII approximation of mathematical “f ∘ g”countLowerCase
’s argument had name str
Function composition can be used almost like Unix pipelines
process = countLowercase . toPigLatin . extractComments . unCompress
Exercise: Write the type of “.
” without typing :t (.)
into ghci
\
variable(s) ->
body\
” is an ASCII approximation of “λ”, so pronounced “lambda”Example:
countLowercaseAndDigits :: String -> Int
countLowercaseAndDigits =
length . filter (\c -> isLower c || isDigit c)
Lambda abstractions can deconstruct values with patterns, e.g.:
... (\(Right x) -> x) ...
+
, *
, /
, .
, ||
, :
_
, and '
add 1 2
1 `add` 2
!#$%&*+./<=>?@\^|-~
or constructors starting “:
”
(+) 1 2
(,)
, (,,)
, (,,,)
, (,,,,)
, etc.Infix functions can be partially applied in a parenthesized section
stripPunctuation :: String -> String
stripPunctuation = filter (`notElem` "!#$%&*+./<=>?@\\^|-~:")
-- Note above string the SECOND argument to notElem ^
..
, :
, ::
, =
, \
, |
, <-
, ->
, @
, ~
, =>
, --
)infixl
/infixr
/infix
for left/right/no associativityelse
clauses, and let
…in
clauses extend as far to the right as possible (meaning they never stop at any infix operator, no matter how low precedence)infixl 9 !! -- This is the default when fixity unspecified
infixr 9 .
infixr 8 ^, ^^, ⋆⋆
infixl 7 ⋆, /, `quot`, `rem`, `div`, `mod`
infixl 6 +, - -- Unary negation "-" has this fixity, too
infixr 5 ++ -- built-in ":" constructor has this fixity, too
infix 4 ==, /=, <, <=, >=, >, `elem`, `notElem`
infixr 3 &&
infixr 2 ||
infixl 1 >>, >>=
infixr 1 =<<
infixr 0 $, $!, `seq`
If you can’t remember, use :i
in GHCI:
Prelude> :i &&
(&&) :: Bool -> Bool -> Bool -- Defined in GHC.Classes
infixr 3 &&
infixl 9
infixr 0
” operators$
is function application, but with lowest precedence
($) :: (a -> b) -> a -> b
f $ x = f x
putStrLn $ "the value of " ++ key ++ " is " ++ show value
seq :: a -> b -> b
just returns value of second argument…main = let q = 1 `div` 0
in seq q $ putStrLn "Hello world!\n" -- exception
seq
has to be built into the compiler$!
combines $
and seq
f $! x = x `seq` f x
n0
stack frames in factorial
:factorial n0 = loop 1 n0
where loop acc n | n > 1 = loop (acc * n) (n - 1)
| otherwise = acc
acc
can contain a chain of thunks n
long(((1 * n) * (n - 1)) * (n - 2) ...)
– Laziness means only evaluated when needed$!
or seq
factorial n0 = loop 1 n0
where loop acc n | n > 1 = (loop $! acc * n) (n - 1)
| otherwise = acc
factorial n0 = loop 1 n0
where loop acc n | n > 1 = acc `seq` loop (acc * n) (n - 1)
| otherwise = acc
stack new
mypackage [new-template
] – generate scaffoldingstack setup
– install GHC, etc. (required even if you have a system GHC)stack build
– build your projectstack exec -- $SHELL
– spawn a shell in which you can run your programstack haddock
somepackage – install documentation for somepackage$BROWSER $(stack path --snapshot-doc-root)/index.html
– view system documentation$BROWSER $(stack path --local-doc-root)/index.html
– view your documentationstack unpack
somepackage – download source for somepackage$HOME/.stack/config.yaml
import
syntaxMain
, as programs start at function main
in Main
Main
, a module named M must reside in a file named M.hs
Main
modulesLet’s add this to the top of our source file
module Main where -- redundant since Main is the default
import System.IO
module
name where
” or “module
name (
exported-symbol[,
…]) where
” (non-exported symbols provide modularity)import
module - imports all symbols in moduleimport qualified
module as
ID - prefixes imported symbols with ID.
import
module (
function1[,
function2 …])
- imports just the named functionsimport
module hiding (
function1[,
function2 …])
- imports all but the named functionsdo
notationLet’s write a function to greet someone
Type the following into a file greet.hs
:
wget
cs240h.stanford.edu/greet1.hs
module Main where
import System.IO
greet h = do
hPutStrLn h "What is your name?"
name <- hGetLine h
hPutStrLn h $ "Hi, " ++ name
withTty = withFile "/dev/tty" ReadWriteMode
main = withTty greet
main
in GHCIdo
notationgreet h = do
hPutStrLn h "What is your name?"
name <- hGetLine h
hPutStrLn h $ "Hi, " ++ name
do
block lets you sequence IO actions. In a do
block:
<-
action - binds pat (variable or constructor pattern) to result of executing actionlet
pat =
pure-value - binds pat to pure-value (no “in
…” required)do
block (i.e., can use <-
, need let
for bindings)do
/let
/case
won’t parse after prefix function
func $ do
…”func (do
…)
”main :: IO ()
greet :: Handle -> IO ()
hPutStrLn :: Handle -> String -> IO ()
hGetLine :: Handle -> IO String
IO
is a parameterized type (just as Maybe
is parameterized)
IO String
” means IO action that produces a String
if executedMaybe
, we won’t use a constructor for IO
, which is somewhat magicWhat if we try to copy a line of input as follows?
main = hPutStrLn stdout (hGetLine stdin)
hPutStrLn
expects type String
, while hGetLine
returns an IO String
IO [String]
to get a [String]
case
, because we don’t have a constructor for IO
… Besides, the order and number of deconstructions of something like hPutStr
matters<-
operator in do
blocks!do name <- hGetLine h
hPutStrLn h $ "Hi, " ++ name
hGetLine
and hPutStrLn
return IO
actions that can change the world
main
action is ever executeddo name <- hGetLine h
hPutStrLn h $ "Hi, " ++ name
do
block builds a compound action from other actions
IO a
actions to the world, extracting values of type a
a
greet
$ ghc --make -dynamic greet
[1 of 1] Compiling Main ( greet.hs, greet.o )
Linking greet ...
$ ./greet
What is your name?
David
Hi, David
Note -dynamic
flag allows GHCI to use compiled object code
$ ghci ./greet.hs
...
Prelude Main> :show modules
Main ( greet1.hs, greet1.o )
Prelude Main>
*
before Main
means compiled. For access to internal symbols, say:Prelude Main> :load *greet.hs
[1 of 1] Compiling Main ( greet.hs, interpreted )
Ok, modules loaded: Main.
*Main> :show modules
Main ( greet1.hs, interpreted )
*Main>
-dynamic
flag was for example, don’t generally need itreturn
functiongreet
to return the name of the person?
hPutStrLn :: IO ()
; want to end with action returning name
This does not work:
do ...
hPutStrLn h $ "Hi, " ++ name
name -- Incorrect, will not compile
Problem: every action in an IO
do block must have type IO a
for some a
Solution: return
function gives trivial IO
action returning a particular value
greet :: Handle -> IO String
greet h = do
hPutStrLn h "What is your name?"
name <- hGetLine h
hPutStrLn h $ "Hi, " ++ name
return name
Note: return
is not control flow statement, just a function
return :: a -> IO a
.
” (fixity infixr 9
)Function >>=
(pronounced “bind”) allows point-free IO composition
(>>=) :: IO a -> (a -> IO b) -> IO b
infixl 1 >>=
Let’s re-write greet
with point-free style to avoid variable name
greet h = do
hPutStrLn h "What is your name?"
hGetLine h >>= hPutStrLn h . ("Hi, " ++)
>>=
composes left-to-right, while .
goes right-to-leftdo
blocks are just syntactic sugar for calling >>=
-- Desugared version of original greet:
greet h = hPutStrLn h "What is your name?" >>= \_ ->
hGetLine h >>= \name ->
hPutStrLn h ("Hi, " ++ name)
Handle
, tell user whether s/he won/lost/tiedcomputerVsUser :: Move -> Handle -> IO ()
Starter code: wget
cs240h.stanford.edu/rock1.hs
Example:
*Main> withTty $ computerVsUser Rock
Please enter one of [Rock,Paper,Scissors]
garbage
Please enter one of [Rock,Paper,Scissors]
Paper
You Win
*Main> withTty $ computerVsUser Scissors
Please enter one of [Rock,Paper,Scissors]
Paper
You Lose
getMove :: Handle -> IO Move
getMove h = do
hPutStrLn h $ "Please enter one of " ++ show ([minBound..] :: [Move])
-- Here is the added code:
input <- hGetLine h
case parseMove input of Just move -> return move
Nothing -> getMove h
computerVsUser :: Move -> Handle -> IO ()
computerVsUser computerMove h = do
userMove <- getMove h
let o = outcome userMove computerMove
hPutStrLn h $ "You " ++ show o
id :: a -> a
id x = x
const :: a -> b -> a
const a _ = a
fst :: (a, b) -> a
fst (a, _) = a
snd :: (a, b) -> b
snd (_, b) = b
print a = putStrLn (show a) -- what's the type? a -> IO ()?
show a = ??? -- how to implement?
id :: a -> a
just passes the value through1 + 1
and 1.0 + 1.0
compute very different functionsshow
converts value to String
, depends entirely on input typederiving Show
” in declarations)show
a function (type Int -> Int
)Ad-hoc polymorphic functions are called methods and declared with classes
class MyShow a where
myShow :: a -> String
The actual method for each type is defined in an instance declaration
data Point = Point Double Double
instance MyShow Point where
myShow (Point x y) = "(" ++ show x ++ ", " ++ show y ++ ")"
What’s the type of a function that calls myShow
? Ask GHCI:
myPrint x = putStrLn $ myShow x
*Main> :t myPrint
myPrint :: MyShow a => a -> IO ()
(
class type-var, …) =>
” at start of type, E.g.:myPrint :: MyShow a => a -> IO ()
sortAndShow :: (Ord a, MyShow a) => [a] -> String
elem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) = x==y || elem x ys
add :: (Num a) => a -> a -> a
add arg1 arg2 = arg1 + arg2
myPrint
, you explicitly give it a value of type a
a
’s MyShow
instanceLet’s say you want to cache result of super-expensive function
superExpensive val = len $ veryExpensive (val :: Int)
where len [] = 0
len (x:xs) = 1 + len xs
cachedResult = superExpensive 5
cachedResult
will start as thunk, be executed once, then contain valueLet’s think about the types
*Main> :t superExpensive
superExpensive :: Num a => Int -> a
*Main> :t cachedResult
cachedResult :: Integer
superExpensive
can return any Num
you wantcachedResult :: (Num a) => a
?cachedResult
into a function, undermining our caching goal!f
), rather than a pattern ((x, y) =
…, (Just x) =
…)f x =
… ok, f =
… not)Num
, try Integer
then Double
(this sequence can be changed with a default
declaration)This code will compile
-- Compiler infers: show1 :: (Show x) => x -> String
show1 x = show x
But neither of these will:
show2 = show
show3 = \x -> show x
Add type signatures to functions—a good idea anyway for top-level bindings, and sometimes necessary for let
bindings
-- No problem, compiler knows you want ad hoc polymorphism
show2 :: (Show x) => x -> String
show2 = show
Eq
contains ‘==’ and ‘/=’ methods, while Ord
contains <
, >=
, >
, <=
, etc.Ord
instance not also be an Eq
instanceOrd
declares Eq
as a superclass, using a context
class Eq a => Ord a where
(<), (>=), (>), (<=) :: a -> a -> Bool
a <= b = a == b || a < b -- default methods can use superclasses
....
Ord
dictionary can lookup the Eq
dictionarymyShow
for a list of items whose type is of class MyShow
instance (MyShow a) => MyShow [a] where
myShow [] = "[]"
myShow (x:xs) = myShow x ++ ":" ++ myShow xs
Functor
is a class for parameterized types onto which you can map functions:
class Functor f where
fmap :: (a -> b) -> f a -> f b
f
, rather types f a
and f b
An example of a Functor
is Maybe
:
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude> fmap (+ 1) Nothing
Nothing
Prelude> fmap (+ 1) $ Just 2
Just 3
Functor
sLists are a Functor
[]
can be used as a prefix type constructor (“[] Int
” means “[Int]
”) and can be used to declare instancesmap :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
instance Functor [] where
fmap = map
IO
is a Functor
instance Functor IO where
fmap f io = io >>= return . f
-- equivalent to: do val <- io; return (f val)
greet h = do
hPutStrLn h "What is your name?"
fmap ("Hi, " ++) (hGetLine h) >>= hPutStrLn h
What happens if you try to make an instance of Functor
for Int
?
instance Functor Int where -- compilation error
fmap _ _ = error "placeholder"
fmap :: (a -> b) -> Int a -> Int b
, but Int
not parameterizedInt
, Double
, ()
) directly describes valuesMaybe
, []
, IO
)Either
, (,)
)Int
, Double
, ()
, etc.)Maybe
, IO
, etc.)Either
)Monad
classreturn
and >>=
are actually methods of a class called Monad
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
fail :: String -> m a -- called when pattern binding fails
fail s = error s -- default is to throw exception
(>>) :: m a -> m b -> m b
m >> k = m >>= \_ -> k
do
blocks for non-IO purposesMonad
—invent a new monad, and you can still use much existing codeMaybe
monadSystem libraries define a Monad
instance for Maybe
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= _ = Nothing
return = Just
fail _ = Nothing
Nothing
to indicate failure
extractA :: String -> Maybe Int
extractB :: String -> Maybe String
...
parseForm :: String -> Maybe Form
parseForm raw = do
a <- extractA raw
b <- extractB raw
...
return (Form a b ...)
IO
threaded WorldNothing
Some data types have a large number of fields
-- Argument to createProcess function
data CreateProcess = CreateProcess CmdSpec (Maybe FilePath)
(Maybe [(String,String)]) StdStream StdStream StdStream Bool
Haskell let define field labels (like C struct
s)
data CreateProcess = CreateProcess {
cmdspec :: CmdSpec,
cwd :: Maybe FilePath,
env :: Maybe [(String,String)],
std_in :: StdStream,
std_out :: StdStream,
std_err :: StdStream,
close_fds :: Bool
}
Let’s add field labels to our Point
class
data Point = Point { xCoord :: Double, yCoord :: Double }
data Point = Point { xCoord :: Double, yCoord :: Double }
Can initialize a type with labels by naming fields
myPoint = Point { xCoord = 1.0, yCoord = 1.0 }
undefined
- a thunk that throws an exceptionCan also pattern-match on any subset of fields
-- Note the pattern binding assigns the variable on the right of =
getX Point{ xCoord = x } = x
As-patterns are handy to bind a variable and pattern simultaneously (with @
):
getX' p@Point{ xCoord = x }
| x < 100 = x
| otherwise = error $ show p ++ " out of range"
-- As patterns also work without labels
getX' p@(Point x _) = ...
processString s@('$':_) = ...
processString s = ...
Can use field labels as access functions
getX point = xCoord point
xCoord
works anywhere you can use a function of type Point -> Double
There is a special syntax for updating one or more fields
setX point x = point { xCoord = x }
setXY point x y = point { xCoord = x, yCoord = y }
Obviously doesn’t update destructively, but returns new, modified Point
Very handy to maintain state in tail recursive functions and Monads
Another idiom: construct values by customizing some defaultValue
, makes it easy to add more fields later on
A !
before a data field type makes it strict - i.e., can’t be thunk
data State = State !Int Int
data AlgState = AlgState { accumulator :: !Int
, otherValue :: Int }
In both cases above, the first Int
cannot hold a thunk, but only a value
When initializing a datatype using labels, it is mandatory to initialize all strict fields (since they cannot hold the undefined
thunk).
High-level Stream (TCP & Unix-domain) socket support in Network
connectTo :: HostName -> PortID -> IO Handle
listenOn :: PortID -> IO Socket
accept :: Socket -> (Handle, HostName, PortNumber)
sClose :: Socket -> IO ()
Low-level BSD socket functions in Network.Socket
socket :: Family -> SocketType -> ProtocolNumber -> IO Socket
connect :: Socket -> SockAddr -> IO ()
bindSocket :: Socket -> SockAddr -> IO ()
listen :: Socket -> Int -> IO ()
accept :: Socket -> IO (Socket, SockAddr) -- not same accept as above
getAddrInfo
looks up hostnames just like [RFC3493] (returns [
AddrInfo
]
)
We’ll stick to the higher-level functions today
Instead of withTty
, let’s define withClient
that uses TCP:
wget
cs240h.stanford.edu/rock2.hs
withClient :: PortID -> (Handle -> IO a) -> IO a
withClient listenPort fn = do
...
Try it like this:
*Main> withClient (PortNumber 1617) (computerVsUser Rock)
$ nc localhost 1617
Please enter one of [Rock,Paper,Scissors]
Rock
You Tie
withClient :: PortID -> (Handle -> IO a) -> IO a
withClient listenPort fn = do
s <- listenOn listenPort
(h, host, port) <- accept s
putStrLn $ "Connection from host " ++ host ++ " port " ++ show port
sClose s -- Only accept one client
a <- fn h
hClose h
return a