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 addParentheses 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, 5x = 5
x = 6 -- error, cannot re-bind xsafeDiv 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 0x = 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 1This Haskell code requires n stack frames
factorial n = if n > 1 then n * factorial (n-1) else 1factorial 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 nloop 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 = TrueBindings 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 = acclet, 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 = accEasier 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 = accfactorial n0 = loop 1 n” causes compile errorBool - either True or FalseChar - 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 + arg2add 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 ShowPointT with constructor PointC containing two Doublesderiving Show means you can print the type (helpful in GHCI)Read, Eq, Ord, Enum, BoundedTypes and constructors can use the same name (often do), E.g.:
data Point = Point Double Double deriving ShowOne type can have multiple constructors (like a tagged union):
data Point = Cartesian Double Double
| Polar Double Double
deriving Showdata 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.0data Color = Red | Green | Blue | Violet deriving (Show, Eq, Enum)
myColor :: Color
myColor = Redcase 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 = yisRed :: Color -> Bool
isRed Red = True -- Only matches constructor Red
isRed c = False -- Lower-case c just a variableGiven 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 = Losedata Maybe a = Just a
| Nothingdata Either a b = Left a
| Right bYou 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 = FalseisRed 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 _ _ = Nothingthough often there is a simpler way
addMaybes (Just x) (Just y) = Just (x + y)
addMaybes _ _ = NothingWe 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 IntegerList 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 rangeString is just a list of Char, so ['a', 'b', 'c'] == "abc"You can pattern match on literal lists and Strings
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 ++ yslength :: [a] -> Int -- This code is from language spec
length [] = 0 -- GHC implements differently, why?
length (_:l) = 1 + length lfilter :: (a -> Bool) -> [a] -> [a]
filter pred [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xsNote function error :: String -> a reports assertion failures
deriving Read and readsderiving 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,"")]readsWrite a function to parse moves:
parseMove :: String -> Maybe MoveJust move on successful parse, Nothing otherwiseExamples of use:
*Main> parseMove "Rock"
Just Rock
*Main> parseMove "Paper"
Just Paper
*Main> parseMove "Scissors plus extra junk"
NothingUse reads:
parseMove :: String -> Maybe Move
parseMove str | [(m, "")] <- reads str = Just m
| otherwise = Nothingreads 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 _ = NothingNote 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"
Nothinglength function?haskell.org” is too long for me—I change to “ho”Foldable (we’ll talk about classes later)List.lengthlenAcc is tail recursiveFoldableHere’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 Chars 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 isLowerThe “.” 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 strFunction composition can be used almost like Unix pipelines
process = countLowercase . toPigLatin . extractComments . unCompressExercise: 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 21 `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 9infixr 0” operators$ is function application, but with lowest precedence
($) :: (a -> b) -> a -> b
f $ x = f x putStrLn $ "the value of " ++ key ++ " is " ++ show valueseq :: a -> b -> b just returns value of second argument…main = let q = 1 `div` 0
in seq q $ putStrLn "Hello world!\n" -- exceptionseq has to be built into the compiler$! combines $ and seq
f $! x = x `seq` f xn0 stack frames in factorial:factorial n0 = loop 1 n0
where loop acc n | n > 1 = loop (acc * n) (n - 1)
| otherwise = accacc can contain a chain of thunks n long(((1 * n) * (n - 1)) * (n - 2) ...) – Laziness means only evaluated when needed$! or seqfactorial n0 = loop 1 n0
where loop acc n | n > 1 = (loop $! acc * n) (n - 1)
| otherwise = accfactorial n0 = loop 1 n0
where loop acc n | n > 1 = acc `seq` loop (acc * n) (n - 1)
| otherwise = accstack 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.yamlimport syntaxMain, as programs start at function main in MainMain, a module named M must reside in a file named M.hsMain modulesLet’s add this to the top of our source file
module Main where -- redundant since Main is the default
import System.IOmodule 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.hsmodule 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 greetmain in GHCIdo notationgreet h = do
hPutStrLn h "What is your name?"
name <- hGetLine h
hPutStrLn h $ "Hi, " ++ namedo 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 StringIO 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 StringIO [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, " ++ namehGetLine and hPutStrLn return IO actions that can change the world
main action is ever executeddo name <- hGetLine h
hPutStrLn h $ "Hi, " ++ namedo block builds a compound action from other actions
IO a actions to the world, extracting values of type aagreet$ 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 nameThis does not work:
do ...
hPutStrLn h $ "Hi, " ++ name
name -- Incorrect, will not compileProblem: 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 nameNote: 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 LosegetMove :: 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 oid :: a -> a
id x = xconst :: a -> b -> a
const a _ = afst :: (a, b) -> a
fst (a, _) = asnd :: (a, b) -> b
snd (_, b) = bprint 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 -> StringThe 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] -> Stringelem :: (Eq a) => a -> [a] -> Bool
elem _ [] = False
elem x (y:ys) = x==y || elem x ysadd :: (Num a) => a -> a -> a
add arg1 arg2 = arg1 + arg2myPrint, you explicitly give it a value of type aa’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 5cachedResult 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 xBut neither of these will:
show2 = show
show3 = \x -> show xAdd 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 = showEq 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 MyShowinstance (MyShow a) => MyShow [a] where
myShow [] = "[]"
myShow (x:xs) = myShow x ++ ":" ++ myShow xsFunctor is a class for parameterized types onto which you can map functions:
class Functor f where
fmap :: (a -> b) -> f a -> f bf, rather types f a and f bAn 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 3FunctorsLists 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 = mapIO 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 hWhat 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 Monadclass 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 >>= \_ -> kdo 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 _ = NothingNothing 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 WorldNothingSome data types have a large number of fields
-- Argument to createProcess function
data CreateProcess = CreateProcess CmdSpec (Maybe FilePath)
(Maybe [(String,String)]) StdStream StdStream StdStream BoolHaskell let define field labels (like C structs)
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 } = xAs-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 pointxCoord works anywhere you can use a function of type Point -> DoubleThere 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 abovegetAddrInfo 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.hswithClient :: 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 TiewithClient :: 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