liftM和mapM函数有什么区别?
答案 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]
...请注意map
和mapM
不同! 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)
liftM
和mapM
完全不同,正如您可以通过其类型及其实现看到的那样:
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设置中应用函数。