beuke.org

A personal blog about computer science topics.

Pure Imperative Haskell
Pure Imperative Code Examples in Haskell
Posted on Aug 27 2018 ~ 5 min read
#haskell  #functional programming  #imperative 

It’s often said that simple things which matter in the real world are quite hard to write in Haskell. And indeed it requires more syntax to program in an imperative mutating style in Haskell when compared to Python or JavaScript.

In this post we will compare these three languages with simple examples and see how much more syntax we need to write in Haskell to do imperative style programming.

FOR Loops

A simple for loop with print to stdout.

In Python:

for i in range(4):
    print(i)

In JavaScript:

for (i=0; i < 4; i++)
    console.log(i)

In Haskell:

forM_ [0..3] $ \->
    print i

Result:

0
1
2
3

The similarities are quiet obvious and none of these snippets is more or less complex than the others. One should note that the for in Python and JavaScript are native language keywords, whereas the Haskell version forM_ is a function.

While True with Sleep

One common task in programming is to create endless loops that does something and then sleeps for a while and repeat. The following example implements a while true loop and logs the current date to a temporary file.

In Python:

while True:                                           # endless loop
    now = datetime.datetime.utcnow().isoformat()      # timestamp
    with open(/tmp/times.txtaas file:         # open file
        file.write(now + "\n")                        # write file
    time.sleep(2)                                     # sleep 2 seconds

In JavaScript:

while(true) {                                         // endless loop
    let now = new Date().toISOString()                // timestamp
    fs.appendFileSync("/tmp/times.txt", now + "\n");  // write file
    await new Promise(done => setTimeout(done, 2000)) // sleep 2 seconds
}

In Haskell:

forever $ do                                           -- endless loop
    now <- getCurrentTime                              -- timestamp
    appendFile "/tmp/times.txt" $ show now ++ "\n"     -- write file
    threadDelay $ 2 * 1000 * 1000                      -- sleep 2 seconds

Result:

$ cat /tmp/timestamps.txt
2018-08-23T20:09:22.553Z
2018-08-23T20:09:24.556Z
2018-08-23T20:09:26.558Z
...

Please note that in order to use the await keyword in JavaScript it is necessary to put it in an async function. I omitted this kind of extra “noise” to make the syntax comparisons of the while loop more clear. I also removed the import statements for Python and Haskell. Haskells main function is also ommited. As we can see there is not much difference between the code examples. The Haskell versions of doing I/O (reading time, writing to file and sleep) is syntactically the shortest one. Of course high expressivenesses, and thus fewer lines of code, does not imply simplicity. A language that is very terse tend to be more complex to understand. That is because it requires the programmer to know the meaning of different short names, standard functions and operators.

Nested Loop over 2D List

In Python:

# list declaration
list2D = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

for x in range(len(list2D)):                       # outer loop
    for y in range(len(list2D[x])):                # inner loop
      list2D[x][y] = 42                            # mutate list at x,y

print(list2D)                                      # print list

In JavaScript:

// list declaration
let list2D = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

for (x=0; x < list2D.length; x++)                  // outer loop
   for (y=0; y < list2D[x].length; y++)            // inner loop
        list2D[x][y] = 42                          // mutate list at x,y

console.log(list2D)                                // print list

In Haskell:

-- list delcaration

let list2D = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

print $ runST $ do                                -- print and run state

  listState <- (mapM . mapM) newSTRef list2D      -- create list state

  forM_ [0..length list2D-1] $ \->              -- outer loop
      forM_ [0..length (list2D !! x)-1] $ \->   -- inner loop
          writeSTRef (listState !! x !! y) 42     -- mutate list state at x,y

  (mapM . mapM) readSTRef listState               -- read (return) state

Result:

[[42,42,42,42],[42,42,42,42],[42,42,42,42]]

As we can see modifying the state of a list is a bit more complex (requires more syntax) in Haskell than in Python or JavaScript. The benefits of this additional syntax in Haskell is that the State is fully encapsulated in the State Monad which makes this computation absolutely pure.[1] This is similar to frameworks like react + redux where you modify the state in a pure manner, but natively available in Haskell and considered default for state manipulation.

The code snippets shows the similarities between the languages. Given the fact that it’s not necessary to develop an deep understanding for mathematical concepts from category theory for e.g. Functor or Monoid, then Haskell can be seen as a nice strongly typed and purely functional programming language with many similarities to scripting languages like JavaScript or Python. So one approach is to learn Haskell in an imperative, pure, functional style and simply ignore the higher concepts of the language until you are either really interested in them or you think you need it.

References