Ответ 1
Функция forM_
более эффективна, так как она не сохраняет результаты операций. Вот и все. (Это имеет смысл только при работе с монадами, потому что чистая функция типа a -> ()
не особенно полезна.)
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 ()
Функция forM_
более эффективна, так как она не сохраняет результаты операций. Вот и все. (Это имеет смысл только при работе с монадами, потому что чистая функция типа a -> ()
не особенно полезна.)
Хорошо,
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
принимает монадическую функцию mf
(имеющую тип Monad m => (a -> m b)
) и применяет ее к каждому элементу в списке xs
; результатом является список внутри монады.
Разница между mapM
и mapM_
заключается в том, что mapM
возвращает список результатов, а mapM_
возвращает пустой результат. Результат каждого действия в mapM_
не сохраняется.
Чтобы понять разницу между (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 your name (" ++ (show i) ++ ")"
name <- getLine
return name
results <- forM [1,2,3] askName
putStrLn $ "Results = " ++ show results
Пример выполнения с forM
:
What your name? (1)
> James
What your name? (2)
> Susan
What your name? (3)
> Alex
Results = ["James", "Susan", "Alex"]
Но если мы изменим forM
на a forM_
, то вместо этого получим:
What your name? (1)
> James
What your name? (2)
> Susan
What 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 your name (" ++ (show i) ++ ")"
name <- getLine
putStrLn $ "Hello! " ++ name
forM [1,2,3] askThenGreet
Пример выполнения с forM
:
What your name? (1)
> Sarah
Hello! Sarah
What your name? (2)
> Dick
Hello! Dick
What your name? (3)
> Peter
Hello! Peter
[(), (), ()]
Общий результат main
исходит из результата forM: [(), (), ()]
. Это довольно бесполезно и досадно, оно появляется в консоли. Но если мы изменим forM
на a forM_
, то вместо этого получим:
What your name? (1)
> Sarah
Hello! Sarah
What your name? (2)
> Dick
Hello! Dick
What your name? (3)
> Peter
Hello! Peter
При этом изменении общий результат исходит от mapM_
и теперь ()
. Это не отображается в консоли (quirk IO monad)! Большой!
Кроме того, используя mapM_
здесь, он более ясен другим читателям вашего кода - вы косвенно объясняете/самодокументируете, что вам не нужны результаты [r1, ..., rn] = [(), (), ()]
- и правильно, так как они бесполезны здесь.