Populating a list with user input I am trying to learn me some Haskell because I found it to be an interesting language. Right now, I am trying to implement a function in Haskell to get acquainted with the language. The function just gets user input and adds the input numbers to a list of floats.

get_budget_values _values _index _length = do
    tmp_value &lt;- getLine

    let new_value = read tmp_value :: Float
    let new_values = new_value : _values
    let new_index = _index + 1

    if new_index &lt; _length
        then get_budget_values new_values new_index _length
        else return new_values


So, is this a proper way to implement a function like that in Haskell? What beginner mistakes am I making? Are there any standard ways of doing something like this?
Add type signatures to top-level functions

You should always add a type signature to top-level functions:

get_budget_values :: [Float] -&gt; Int -&gt; Int -&gt; IO [Float]
get_budget_values _values _index _length = do
    ...


Make functions easy to use and hard to misuse

However, this function's type is weird. We want to get _length values, so why do we have to supply them first? That's error prone. Our funtion should have the type Int -&gt; IO [Float] instead:

get_budget_values :: Int -&gt; IO [Float]
get_budget_values n = go [] 0 n
  where
    go values index len = do
      tmp_value &lt;- getLine

      let new_value = read tmp_value
      let new_values = new_value : values
      let new_index = index + 1

      if new_index &lt; len
          then go new_values new_index len
          else return new_values


Note that due to get_budget_values' type signature we don't have to use :: Float at read temp_value anymore.

Follow naming conventions and use existing library functions

Still, our function is sub-par, since we don't follow the common naming convention (camelCase) on the one hand don't  use library functions for our advantage.

So let's say we have doNTimes :: Int -&gt; IO a -&gt; IO [a] so that doNTimes n action repeats the action n times and collects the results in a list. Then we can write

getBudgetValues :: Int -&gt; IO [Float]
getBudgetValues n = doNTimes n getSingleFloat
  where
    getSingleFloat = do
       tmpValue &lt;- getLine
       return (read tmpValue)


The getLine followed by read approach is so common that there is a function for that, namely readLn:

    getSingleFloat = readLn


We end up with

getBudgetValues :: Int -&gt; IO [Float]
getBudgetValues n = doNTimes n readLn


What's missing is doNTimes. Now, we know that there is replicate :: Int -&gt; a -&gt; [a], which is almost correct, but we would end up with [IO Float] instead of IO [Float]. But there's also sequence :: [IO a] -&gt; IO [a].*

Therefore, we can write

doNTimes :: Int -&gt; IO a -&gt; IO [a]
doNTimes n action = sequence (replicate n action)


The final result

This function is also often used and available as replicateM in Control.Monad. We end up with

import Control.Monad (replicateM)

getBudgetValues :: Int -&gt; IO [Float]
getBudgetValues n = replicateM n readLn


Exercises

Write replicate and sequence yourself without looking at their respective source code. replicate :: Int -&gt; a -&gt; [a] should fulfil the following properties for positive n:

let result = replicate n x
in length result == n &amp;&amp; all (== x) result


For any non-positive n, the list should be empty. 

sequence :: [IO a] -&gt; IO [a] should execute the actions and collect their results, e.g.

sequence [putStrLn "Hello", putStrLn "World"]


should print Hello and World on two lines and return [(),()].



* the actual types are more general