In Haskell, we will typically use recursion instead of loops since mutating loop variables are not allowed in most cases. We will not go into laborious detail on recursion and, instead, will consider a couple of basic examples and look quickly at how we can use higher-level abstractions with higher-order functions.
Consider Main.hs:
sumElements :: [Int] -> Int
sumElements [] = 0
sumElements (h : t) = h + sumElements t
lengthElements :: [String] -> [Int]
lengthElements [] = []
lengthElements (h : t) = length h : lengthElements t
main :: IO ()
main = do
print $ sumElements [10, 20, 30]
print $ lengthElements ["aaa", "aa", "a]Which can run as follows:
> stack runhaskell Hello.hs
60
[3,2,1]
This example illustrates two of the most basic types of recursion patterns, namely folding and mapping. Since these patterns are so common, we will typically use higher-order functions instead:
| Abstraction | Function | Example |
|---|---|---|
| Left-fold | foldl ::(a -> b -> a) -> a -> [b] -> a |
λ> foldl (+) 0 [10, 20, 30]60 |
| Right-fold | foldr ::(a -> b -> b) -> b -> [a] -> b |
λ> foldr (+) 0 [10, 20, 30]60 |
| Map | map ::(a -> b) -> [a] -> [b] |
λ> map length ["aaa", "aa", "a"][3,2,1] |
There are many others, of course. In general, we should prefer the higher-order functions to explicit recursion.
if, then, elseTODO: Do it!