Подтвердить что ты не робот

В чем разница между forM и forM_ в haskell?

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 ()
4b9b3361

Ответ 1

Функция forM_ более эффективна, так как она не сохраняет результаты операций. Вот и все. (Это имеет смысл только при работе с монадами, потому что чистая функция типа a -> () не особенно полезна.)

Ответ 2

Хорошо,

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_ не сохраняется.

Ответ 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 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] = [(), (), ()] - и правильно, так как они бесполезны здесь.