haskell中forM和forM_有什么区别?

时间:2011-07-14 05:52:53

标签: 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 ()

3 个答案:

答案 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中的列表。 mapMmapM_之间的区别在于,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] = [(), (), ()] - 这是正确的,因为它们在这里毫无用处。