Goal of Part One
Process Array Loop by Iterating on List
This is the basic of loop using iteration, with simple array like data structure, the list.
Example of Doing Loop in Haskell With Map
This tutorial/ guidance/ article is one of three parts. These three combined is going to be a long article. So I won’t speak too much. More on codes, than just words.
-
Overview: Preface
-
Part One: List
-
Part Two: Tuple and Dictionary
-
Part Three: Mapping with Function
The first two parts discuss the most common loop, array in part one and hash in part two. Considering that combining map with function is tricky, This deserve this an article of its own in part three. Part three also contains comparation with other Languages, using Real World Function.
Defining List
Haskell has a few Array implementation. One that is very common is List. It is actually a linked list that behaves like an Array.
Let’s consider a list construct, contain sequence number from 1 to 9.
list :: [Int]
list = [1..9]
It is not mandatory to write function declaration. Sometime I put function declaration, just for clarity reason, Also for my personal exercise. It won’t harm anyone. Now we can omit any function declaration for this list.
list = [1..9]
Printing List
list = [1..9]
main = do
print list
putStrLn ""
This will show:
[1,2,3,4,5,6,7,8,9]
More about Printing
We can ignore do clause. do is just a special syntax for sequencing monad. The type of mainmain is IO (). IO is a monad, hence omitting do.
Relax, forget about Monad, and just go on. Omitting do notatin is useful when creating oneliner example. Consider this example of the same code rewritten without do clause. But using then » operator.
print is just putStrLn $ show. And $ is just an operator to avoid closing bracket. Now we can decompose it as below.
main = putStrLn (show [1..9]) >> putStrLn ""
Or using reverse application operator &.
import Data.Function
main = show [1..9] & putStrLn >> putStrLn ""
List Variation
Let’s add other element to this list.
list :: [Int]
list = [1..9] ++ [0]
main = do
putStr "list : "
putStrLn $ show list
Run this code, and this list will be interpreted as output below.
list : [1,2,3,4,5,6,7,8,9,0]
Accessing Index
Operator || can be used to accessing element by index.
main = do
print (list !! 3)
This will show 4, because it is a zero based index:
4
Since list is actually a linked list, it does not rely on indices. For convenience for coder come from other language, zero based indices can be done this way below.
list :: [Int]
list = [1..9] ++ [0]
indices' :: [Int] -> [Int]
indices' l = [0 .. (length l) - 1]
main = do
putStr "list : "
print list
putStr "indices : "
putStrLn $ show $ indices' list
putStrLn ""
Do not worry about the dollar $ operator. It is just a Haskell operator to avoid paranthese. It could be written as putStrLn(show(indices’(list)))
Note that I intentionally using, apostrophe indices' punctuation mark, to differ from indices in Control.Lens library.
This will display:
list : [1,2,3,4,5,6,7,8,9,0]
indices : [0,1,2,3,4,5,6,7,8,9]
Iterate with mapM_
Looping over array in Haskell is this simple. mapM_ discard newly produced list, and use side effect only, in this case print using IO.
main = do
mapM_ print list
Wait….!!! This simple !!…. Yes! I also thought it was harder, but it turn out to be very short. This will map each element, and applied it to print function. Let’s see the output
1
2
3
4
5
6
7
8
9
0
Iterate with Map
If you care about tor produce new list, map is for you.
main = do
print $ map show list
This show will convert each element to string, and boxed it into new list of string.
["1","2","3","4","5","6","7","8","9","0"]
If you come from other language, and too confused about Haskell notation, you may consider this perspective: map(callback_function, array).
map is very useful for transforming one list to another list. However we still need mapM_ or forM_ whenever we need side effect such as display each element using IO.
main = do
mapM_ putStrLn (map show list)
putStrLn ""
forM_ is just like a mapM_, with reverse aguments. It is available in Data.Foldable.
main = do
forM_ (map show list) putStrLn
putStrLn ""
Both codes have output as shown below. They are using side effect of newly produced (map show list).
1
2
3
4
5
6
7
8
9
0
While doing mapM_ or forM_ after mapseems redundant. It is just an example required in this tutorial, not everything have to be printed out.
Chaining Function
If you wish for another challenge you can make a complex one liner.
main = mapM_ (putStr . (": " ++) . show) ([1..9] ++ [0]) >> putStrLn ""
Or using reverse argument forM_ to make it looks like regular loop. With the same result.
main = forM_ ([1..9] ++ [0]) (putStr . (": " ++) . show) >> putStrLn ""
This will show.
: 1: 2: 3: 4: 5: 6: 7: 8: 9: 0
How does it works ? What is this . in (putStr . (": " ++) . show) anyway.
As our need grow, you might desire to use more than one function. The issue is mapM_ only accept one function. The solution is to chain functions with the dot . infix operator. This will accept the sequence of function as a whole compound operation.
Alternative Loop Form
We can rewrite above loop, even better using map. Also with the same result.
main = putStrLn $ concat $ map ((": " ++) . show) ([1..9] ++ [0])
Or concatMap. Still with the same result.
main = putStrLn $ concatMap ((": " ++) . show) ([1..9] ++ [0])
Building Block
If you do not like oneliner, you can decompose this complex line using let clause in do block, or where clause in function.
main = do
let
myFunctionComposition = (putStr . (": " ++) . show)
myList = ([1..9] ++ [0])
in mapM_ myFunctionComposition myList
putStrLn ""
putList :: [Int] -> IO ()
putList myList =
mapM_ myFunctionComposition myList
where
myFunctionComposition = (putStr . (": " ++) . show)
main = do
putList ([1..9] ++ [0])
putStrLn ""
This will produce exactly the same result.
: 1: 2: 3: 4: 5: 6: 7: 8: 9: 0
View Source File:
In “Part Two” we will discuss about Hash (dictionary).
Happy Coding.