Constructing larger plans
Basic sequencing
(>>) :: IO a -> IO b -> IO b
Function that takes two plans and constructs a plan that first executes the first plan, discard its result, then executes the second plan, and returns its result.
Reading two lines
getTwoLines :: IO String
getTwoLines = getLine >> getLine
GHCi> getTwoLines
Line 1.
Line 2.
"Line 2."
Modifying the result of a plan
liftM :: (a -> b) -> IO a -> IO b
Takes a function and a plan. Constructs a plan that executes the given plan, but before returning the result, applies the function.
duplicateLine :: IO String
duplicateLine = liftM (\ x -> x ++ x) getLine
GHCi> duplicateLine
Hello
"HelloHello"
Shouting
GHCi> :t toUpper
toUpper :: Char -> Char
GHCi> toUpper 'x'
'X'
GHCi> liftM (map toUpper) getLine
Hello
"HELLO"
Combining the output of two sequenced plans
liftM2 :: (a -> b -> c) -> IO a -> IO b -> IO c
Takes an operator and two plans. Constructs a plan that executes the two plans in sequence, and uses the operator to combine the two results.
joinTwoLines :: IO String
joinTwoLines = liftM2 (++) getLine getLine
GHCi> joinTwoLines
Hello
world
"Helloworld"
Joining and flipping two lines
flipTwoLines :: IO String
flipTwoLines =
liftM2 (\ x y -> y ++ x) getLine getLine
GHCi> flipTwoLines
Hello
world
"worldHello"
Revisiting the problematic examples
Wrong:
program1 = getLine ++ getLine
program2 = (\ x -> x ++ x) getLine
program3 = (\ x y -> y ++ x) getLine getLine
Better:
joinTwoLines1 = liftM2 (++) getLine getLine
joinTwoLines2 = (\ x -> liftM2 (++) x x) getLine
joinTwoLines3 =
(\ x y -> liftM2 (++) y x) getLine getLine
duplicateLine = liftM (\ x -> x ++ x) getLine
flipTwoLines =
liftM2 (\ x y -> y ++ x) getLine getLine