haskellmonads

Haskell: how to do a loop


I have to read n times and then put it all in a list. I have the following code but it doesn't work. Any Haskell/Functional expert may give me a light?

replicateIO n a = do y <- a 
                 if n < 0
                    then return []
                    else do ys <- (replicateIO n-1 a)
                            return [y:ys]

Compile error:

tres.hs:1:1: error:
    Couldn't match type ‘(->) t0’ with ‘[]’
    Expected type: t -> [[a]]
      Actual type: t -> t0 -> [[a]]
  |
1 | replicateIO n a = if n < 0    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

Solution

  • There are some problems here:

    1. you write [y:ys] as cons, but a cons in Haskell is the (:) :: a -> [a] -> [a] operator, so it is y : ys, or (y : ys) (between brackets). If you write [y:ys], you wrap the result of (y:ys) an a new singleton list;
    2. you perform y <- a n+1 times, since one of the reasons to use Monads can be to use a state (for instance the state of the machine with IO), this can have unintended side effects; and
    3. you use (replicateIO n-1 a) which is interpreted in Haskell as ((replicateIO n)-(1 a)).

    But I think the most important aspect is that you write imperative programs in Haskell syntax. Haskell is a functional programming language. This means a different paradigm. I think it would probably be better to write first some programs that do not work with monads (and do notation).

    That being said, we can use the above remarks to solve the problem in the question:

    replicateIO n a = if n < 0
                        then return []
                        else do y <- a
                                ys <- replicateIO (n-1) a
                                return (y:ys)
    

    Although this fixes the issues, it is not very elegant and Haskell-ish syntax. We can for instance use guards instead of if-then-else:

    replicateIO n a | n < 0 = return []
                    | otherwise = do
                          y <- a
                          ys <- replicateIO (n-1) a
                          return (y:ys)
    

    We can make some further improvements (and even omit the do notation, but I think it is probably beneficial to first realize that do is actually syntactical sugar for some functions).