Search code examples
haskell

How to use map to print a List in Haskell?


Sorry if I am being silly but I am a beginner in Haskell ans I am trying to make a print a List with putStrLn but i am not sure how to solve the next problem:

And I am trying to made a basic print List in Haskell with the code:

import System.IO

array = map show [1, 2, 3, 4]

main :: IO ()
main = do
  map putStrLn array

but the compiler give me:

 8 main.hs:7:3: error:                                                                                                          
  7     • Couldn't match expected type ‘IO ()’ with actual type ‘[IO ()]’                                                            
  6     • In a stmt of a 'do' block: map putStrLn array                                                                              
  5       In the expression: do map putStrLn array                                                                                   
  4       In an equation for ‘main’: main = do map putStrLn array 

How I should fix it?


Solution

  • Haskell handles IO quite differently than other languages.

    In a language like Java, System.Out.println(3) is a statement which does not evaluate to a value, so you can't even write something like x = System.Out.println(3);.

    In a language like Lisp, (print 3) evaluates to 3 and, in the process of evaluation, prints 3. So saying something like (setq x (print 3)) will set the value of x to 3 and also print 3.

    In Haskell, putStrLn "3" represents the command to print 3. Thus, saying x = putStrLn "3" does absolutely nothing except assign x to the command putStrLn "3".

    Let's look at some types. We have

    map :: (a -> b) -> [a] -> [b]
    putStrLn :: String -> IO ()
    

    Thus, we should have

    map putStrLn :: [String] -> [IO ()]
    map putStrLn array :: [IO ()]
    

    In other words, map putStrLn array is a list of "IO actions" which result in a value of type () (basically, this means that executing the actions results in no extra information).

    However, in order to write

    main :: IO ()
    main = map putStrLn array
    

    which is what

    main :: IO ()
    main = do map putStrLn array
    

    translates to, we need map putStrLn to be of type IO (), NOT of type [IO ()].

    If you wish to execute an action for each element of a list, you can use the for_ function, which has type for_ :: (Foldable g, Applicative f) => g a -> (a -> f ()) -> f (). IO is Applicative and [] is Foldable, so in particular for_ :: [String] -> (String -> IO ()) -> IO () is one type that for_ can take. The code looks like

    import Data.Foldable (for_)
    
    array :: [String]
    array = map show [1, 2, 3, 4]
    
    main :: IO ()
    main = for_ array putStrLn
    

    This would be equivalent in an imperative language to

    for each x in array do {
        putStrLn x;
    }