在Haskell中liftM和mapM有什么区别

时间:2011-05-02 11:43:57

标签: haskell functional-programming monads combinators

liftM和mapM函数有什么区别?

4 个答案:

答案 0 :(得分:30)

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

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

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

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

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

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

打印:

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

它正在做的是迭代列表,将(putStrLn . show)应用于列表中的每个元素(具有打印出每个数字的IO效果),并将数字转换为{{1}价值。结果列表包含() - [(), (), ()]的输出。

答案 1 :(得分:21)

首先,类型不同:

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

liftM将类型a -> b的函数提升为monadic对应项。 mapM应用一个函数,该函数为值列表生成monadic值,从而生成嵌入monad中的结果列表。

示例:

> liftM (map toUpper) getLine
Hallo
"HALLO"

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

...请注意mapmapM不同! E.g。

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

答案 2 :(得分:9)

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

您还可能会看到运营商<$>被用作fmap的同义词。

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

答案 3 :(得分:5)

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将monadic函数应用于列表的每个元素时,liftM会在monadic设置中应用函数。