HLint suggests that I use forM_
rather than forM
. Why? I see they have different type signatures but haven't found a good reason to use one over the other.
forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b)
forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m ()
The forM_
function is more efficient because it does not save the results of the operations. That is all. (This only makes sense when working with monads because a pure function of type a -> ()
is not particularly useful.)
Ok,
forM is mapM with its arguments flipped.
forM_ is mapM_ with its arguments flipped.
Let's see in mapM and mapM_ :
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM mf xs
takes a monadic function mf
(having type Monad m => (a -> m b)
) and applies it to each element in list xs
; the result is a list inside a monad.
The difference between mapM
and mapM_
is, that mapM
returns a list of the results, while mapM_
returns an empty result. The result of each action in mapM_
is not stored.
To understand the difference between (A): forM xs f
and (B): forM_ xs f
, it might help to compare the difference between the following:
-- Equivalent to (A)
do
r1 <- f x1
r2 <- f x2
...
rn <- f xn
return [r1, r2, ..., rn]
-- Equivalent to (B)
do
_ <- f x1
_ <- f x2
...
_ <- f xn
return ()
The crucial difference being that forM_
ignores the results r1
, ... rn
and just returns an empty result via return ()
. Think of the underscore as meaning "don't care" ... forM_
doesn't care about the results. forM
however, does care about the results and returns them in as a list via return [r1, r2, ... rn]
.
Example 1 The code below asks for your name three times and prints the results of the forM.
import Control.Monad (forM, forM_)
main = do
let askName i = do
putStrLn $ "What's your name (" ++ (show i) ++ ")"
name <- getLine
return name
results <- forM [1,2,3] askName
putStrLn $ "Results = " ++ show results
An example execution with forM
:
What's your name? (1)
> James
What's your name? (2)
> Susan
What's your name? (3)
> Alex
Results = ["James", "Susan", "Alex"]
But if we change the forM
to a forM_
, then we would have instead:
What's your name? (1)
> James
What's your name? (2)
> Susan
What's your name? (3)
> Alex
Results = ()
In your case, the linter is telling you that you're not using the return values of your forM
(you don't have foo <- forM xs f
, you probably have forM xs f
by itself on a line) and so should use forM_
instead. This happens, for
example, when you are using a monadic action like putStrLn
.
Example 2 The code below asks for your name and then says "Hello" – repeating three times.
import Control.Monad (forM, forM_)
main = do
let askThenGreet i = do
putStrLn $ "What's your name (" ++ (show i) ++ ")"
name <- getLine
putStrLn $ "Hello! " ++ name
forM [1,2,3] askThenGreet
An example execution with forM
:
What's your name? (1)
> Sarah
Hello! Sarah
What's your name? (2)
> Dick
Hello! Dick
What's your name? (3)
> Peter
Hello! Peter
[(), (), ()]
The overall result of main
comes from the result of the forM: [(), (), ()]
. It's pretty useless and annoyingly, it appears in the console. But if we change the forM
to a forM_
, then we would have instead:
What's your name? (1)
> Sarah
Hello! Sarah
What's your name? (2)
> Dick
Hello! Dick
What's your name? (3)
> Peter
Hello! Peter
With that change, the overall result comes from the mapM_
and is now ()
. This doesn't show up in the console (a quirk of the IO monad)! Great!
Also, by using mapM_
here, it's clearer to other readers of your code – you're indirectly explaining / self-documenting that you don't care about the results [r1, ..., rn] = [(), (), ()]
– and rightly so as they're useless here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With