【问题标题】:What is the difference between liftM and mapM in HaskellHaskell中的liftM和mapM有什么区别
【发布时间】:2011-05-02 11:43:57
【问题描述】:

函数liftM和mapM有什么区别?

【问题讨论】:

  • @larsmans 这个问题与mapM无关。
  • @mgiuca:你是对的。打扰一下。我想我应该更仔细地阅读。

标签: haskell functional-programming monads combinators


【解决方案1】:

它们并不真正相关。我将尝试解释他们每个人的作用。我假设你对什么是 monad 有基本的了解。

liftM :: Monad m => (a -> b) -> (m a -> m b) 允许您在 monad 中使用普通函数。它接受一个函数a -> b,并将其转换为一个函数m a -> m b,它的作用与原始函数完全相同,但它是在一个单子中完成的。结果函数对 monad 没有“做”任何事情(它不能,因为原始函数不知道它在 monad 中)。例如:

main :: IO ()
main = do
    output <- liftM ("Hello, " ++) getLine
    putStrLn output

函数("Hello, " ++) :: String -&gt; String 将“Hello,”添加到字符串中。将它传递给liftM 会创建一个IO String -&gt; IO String 类型的函数——现在你有了一个在IO monad 中工作的函数。它不做任何 IO,但它可以将一个 IO 动作作为输入,并产生一个 IO 动作作为输出。因此,我可以将getLine 作为输入传递,它会调用getLine,在结果前面加上“Hello”,并将其作为IO 操作返回。

mapM :: Monad m =&gt; (a -&gt; m b) -&gt; [a] -&gt; m [b] 完全不同;请注意,与liftM 不同,它需要一个单子函数。例如,在 IO monad 中,它的类型为 (a -&gt; IO b) -&gt; [a] -&gt; IO [b]。它与普通的map 函数非常相似,只是它将一个单子操作应用于列表,并生成一个包含在单子操作中的结果列表。例如(一个非常糟糕的):

main2 :: IO ()
main2 = do
    output <- mapM (putStrLn . show) [1, 2, 3]
    putStrLn (show output)

打印出来:

1
2
3
[(),(),()]

它所做的是遍历列表,将(putStrLn . show) 应用于列表中的每个元素(具有打印出每个数字的IO 效果),并将数字转换为() 值。结果列表由[(), (), ()] 组成——putStrLn 的输出。

【讨论】:

  • 另外,如果你只关心mapM的一元效应,而不关心返回的列表,你可以使用mapM_mapM_ 函数的类型为 Monad m =&gt; (a -&gt; m b) -&gt; [a] -&gt; m (),它在 putStrLn 示例中很有用,您可能对单位列表不感兴趣。
  • @Tom Lokhorst 是的,没错。 (我想不出一个有用的例子来说明结果对 IO monad 很重要。)
  • mapM为什么要把数字转换成'()'?
  • @Luke putStrLn :: String -&gt; IO () 返回一个单位值(即())。这是另一个示例:mapM (\x -&gt; putStrLn (show x) &gt;&gt; return (x + 1)) [1, 2, 3]。这将返回一个 Ints 列表而不是单位。
【解决方案2】:

首先,类型不同:

liftM :: (Monad m) => (a -> b) -> m a -> m b
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

liftMa -&gt; b 类型的函数提升为一元对应物。 mapM 将产生单子值的函数应用于值列表,从而产生嵌入在单子中的结果列表。

例子:

> liftM (map toUpper) getLine
Hallo
"HALLO"

> :t mapM return "monad"
mapM return "monad" :: (Monad m) => m [Char]

...请注意mapmapM 不同!例如

> map (x -> [x+1]) [1,2,3]
[[2],[3],[4]]
> mapM (x -> [x+1]) [1,2,3]
[[2,3,4]]

【讨论】:

  • map 的 lambda 中是 (\x -&gt; [x + 1]) 吗?我尝试了 x 不带反斜杠的语法,得到Pattern syntax in expression context: x -&gt; [x + 1]。我是 Haskell 新手,所以我可能会在这里遗漏一些东西。
【解决方案3】:

其他答案已经很好地解释了,所以我只想指出,在真正的 Haskell 代码中,您通常会看到 fmap 而不是 liftM,因为 fmap 只是该类型中更通用的版本类Functor。由于所有行为良好的Monads 也应该是Functor 的实例,它们应该是等价的。

您可能还会看到运算符&lt;$&gt; 用作fmap 的同义词。

另外,mapM f = sequence . map f,因此您可以将其视为将值列表转换为操作列表,然后依次运行操作,将结果收集到列表中。

【讨论】:

    【解决方案4】:

    liftMmapM 完全不同,您可以从它们的类型和实现中看出:

    mapM         :: Monad m => (a -> m b) -> [a] -> m [b]
    mapM f as    =  sequence (map f as)
    
    liftM        :: (Monad m) => (a1 -> r) -> m a1 -> m r
    liftM f m1   = do { x1 <- m1; return (f x1) }
    

    因此,mapM 将单子函数应用于列表的每个元素,liftM 将函数应用于单子设置。

    【讨论】:

      猜你喜欢
      • 2023-03-27
      • 2021-06-27
      • 1970-01-01
      • 2017-11-27
      • 1970-01-01
      • 2016-08-07
      • 2016-05-16
      • 1970-01-01
      • 2017-06-17
      相关资源
      最近更新 更多