HLint建议我使用forM_
而不是forM
。为什么?我看到他们有不同类型的签名,但没有找到使用其中一个的好理由。
forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b)
forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m ()
答案 0 :(得分:25)
forM_
函数效率更高,因为它不会保存操作的结果。就这些。 (这在使用monad时才有意义,因为a -> ()
类型的纯函数不是特别有用。)
答案 1 :(得分:11)
确定,
forM is mapM with its arguments flipped.
forM_ is mapM_ with its arguments flipped.
让我们在mapM和mapM_中看到:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM mf xs
采用monadic函数mf
(类型为Monad m => (a -> m b)
)并将其应用于列表xs
中的每个元素;结果是monad中的列表。
mapM
和mapM_
之间的区别在于,mapM
返回结果列表,而mapM_
返回空结果。不存储mapM_
中每个操作的结果。
答案 2 :(得分:3)
要了解(A):forM xs f
和(B):forM_ xs f
之间的区别,可能有助于比较之间的区别以下内容:
-- 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 ()
关键区别是forM_
会忽略结果r1
,... rn
,只会通过return ()
返回空结果。将下划线视为“不关心”...... forM_
不关心结果。 forM
但是, 关心结果并通过return [r1, r2, ... rn]
将其作为列表返回。
示例1 下面的代码要求你的名字三次并打印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
使用forM
执行示例:
What's your name? (1)
> James
What's your name? (2)
> Susan
What's your name? (3)
> Alex
Results = ["James", "Susan", "Alex"]
但如果我们将forM
更改为forM_
,那么我们就会改为:
What's your name? (1)
> James
What's your name? (2)
> Susan
What's your name? (3)
> Alex
Results = ()
在你的情况下,linter告诉你,你没有使用forM
的返回值(你没有foo <- forM xs f
,你自己可能有forM xs f
在一条线上)所以应该使用forM_
代替。这发生了,因为
例如,当您使用putStrLn
等单一操作时。
示例2 下面的代码询问您的姓名,然后说“你好” - 重复三次。
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
使用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
[(), (), ()]
main
的总体结果来自forM的结果:[(), (), ()]
。它非常无用且令人讨厌,它出现在控制台中。但是,如果我们将forM
更改为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
通过此更改,整体结果来自mapM_
,现在是()
。这不会出现在控制台中(IO monad的怪癖)!大!
此外,通过在此使用mapM_
,您的代码的其他读者会更清楚 - 您间接地解释/自我记录您不关心结果[r1, ..., rn] = [(), (), ()]
- 这是正确的,因为它们在这里毫无用处。