Search code examples
loopshaskellwhile-loopmonadsio-monad

Example of while loop in Haskell using monads


I want to write a loop in haskell using monads but I am having a hard time understanding the concept.

Could someone provide me with one simple example of a while loop while some conditions is satisfied that involves IO action? I don't want an abstract example but a real concrete one that works.


Solution

  • Below there's a horrible example. You have been warned.

    Consider the pseudocode:

    var x = 23
    while (x != 0) {
       print "x not yet 0, enter an adjustment"
       x = x + read()
    }
    print "x reached 0! Exiting"
    

    Here's its piece-by-piece translation in Haskell, using an imperative style as much as possible.

    import Data.IORef
    
    main :: IO ()
    main = do
       x <- newIORef (23 :: Int)
       let loop = do
              v <- readIORef x
              if v == 0
              then return ()
              else do
                 putStrLn "x not yet 0, enter an adjustment"
                 a <- readLn
                 writeIORef x (v+a)
                 loop
       loop
       putStrLn "x reached 0! Exiting"
    

    The above is indeed horrible Haskell. It simulates the while loop using the recursively-defined loop, which is not too bad. But it uses IO everywhere, including for mimicking imperative-style mutable variables.

    A better approach could be to remove those IORefs.

    main = do
       let loop 0 = return ()
           loop v = do
              putStrLn "x not yet 0, enter an adjustment"
              a <- readLn
              loop (v+a)
       loop 23
       putStrLn "x reached 0! Exiting"
    

    Not elegant code by any stretch, but at least the "while guard" now does not do unnecessary IO.

    Usually, Haskell programmers strive hard to separate pure computation from IO as much as possible. This is because it often leads to better, simpler and less error-prone code.