Search code examples
listhaskellintegerstdin

How to initialize a list of integers from stdin reading each integer on a separate line in haskell?


I am a newbie in Haskell but I know C++ and Java. now I wonder how can I read a list of integers similar to this psuedocode?

   cout << "Please enter the size of your list";
   cin >> list_size;
   int mylist[listsize];
   for (int i = 0 ; i<list_size; i++) {
      cout<< "please enter the next number:";
      cin>> number;
     mylist[i]=number;
    }

Solution

  • The answers given in the linked question use lazy IO (through getContents).

    Personally, I don't like lazy IO, and I think trying to understand how it works in combination with do-notation is a recipe for confusion. So here's an answer that doesn't use lazy IO:

    import Control.Monad(replicateM)
    
    main :: IO ()
    main = do
        putStrLn "Please enter the size of your list"
        times <- readLn
        ns <- replicateM times 
                         (do putStrLn "please enter the next number: "
                             readLn :: IO Int) -- type annotation to remove ambiguity
        print ns
    

    replicateM takes a number n and an effectful action, and returns a new action that executes the original n times and returns a list with the results.

    What if I want to display a different prompt message for each number?

    That can be understood as an effectful transformation on the list of prompt messages, one that substitutes each message by the value entered. A kind of "effectful map".

    The function traverse can be used here. Or perhaps for, which is the flipped version of traverse:

    {-# language ScopedTypeVariables #-}
    
    import Data.Traversable (for)
    
    main :: IO ()
    main = do
        putStrLn "Please enter the size of your list"
        times :: Int <- readLn
        ns <- for [1..times]
                  (\prompt -> do putStrLn ("enter " ++ show prompt)
                                 readLn :: IO Int) -- type annotation to remove ambiguity
        print ns
    

    The :: IO Int annotation is required in the example because readLn can read any type that has a Read instance, but we aren't doing anything Int-specific with the results, so we need to inform the compiler about the type somehow.