let and where
Prerequisites: Pattern matching
Both let and where name intermediate values so expressions stay
readable.
where: definitions after the expression
cylinderArea :: Double -> Double -> Double
cylinderArea r h = sides + 2 * top
where
sides = 2 * pi * r * h
top = pi * r ^ 2
main :: IO ()
main = print (cylinderArea 1 2)
18.84955592153876
where reads like a mathematical definition: "the area is the sides
plus two tops, where sides is ... and top is ...". Its definitions can
see the function's arguments and are shared by all the guards of that
equation.
let ... in: definitions before the expression
cylinderArea :: Double -> Double -> Double
cylinderArea r h =
let sides = 2 * pi * r * h
top = pi * r ^ 2
in sides + 2 * top
main :: IO ()
main = do
print (cylinderArea 1 2)
print (let x = 3; y = 4 in x * y)
18.84955592153876
12
let ... in is an expression, so it can go anywhere a value can.
Inside a do block, you write let without in, as you've seen
already.
Helper functions in where
where can define functions too, and pattern match:
initials :: String -> String -> String
initials first last' = [f] ++ ". " ++ [l] ++ "."
where
(f : _) = first
(l : _) = last'
report :: [Int] -> String
report xs = "count " ++ fmt (length xs) ++ ", total " ++ fmt (sum xs)
where
fmt n = "<" ++ show n ++ ">"
main :: IO ()
main = do
putStrLn (initials "Grace" "Hopper")
putStrLn (report [4, 5, 6])
G. H.
count <3>, total <15>
- A trailing apostrophe is a legal character in names:
last'avoids clashing with the Prelude'slast. (x'is read "x prime".) - Indentation matters: everything in a
whereblock must line up.
Which to use?
It's mostly style. where is more common for function helpers and works
across guards; let is handy for a quick local value inside an
expression and inside do blocks.