函数liftM和mapM之间有什么区别?
发布于 2011-05-02 19:56:26
首先,类型不同:
liftM :: (Monad m) => (a -> b) -> m a -> m b
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
liftM
将a -> b
类型的函数提升为一元对应函数。mapM
将一个函数应用于一个值列表,该函数产生一个一元值,从而产生嵌入在一元值中的结果列表。
示例:
> liftM (map toUpper) getLine
Hallo
"HALLO"
> :t mapM return "monad"
mapM return "monad" :: (Monad m) => m [Char]
..。注意,map
和mapM
是不同的!例如。
> map (x -> [x+1]) [1,2,3]
[[2],[3],[4]]
> mapM (x -> [x+1]) [1,2,3]
[[2,3,4]]
发布于 2011-05-02 20:00:15
它们并不是真正相关的。我将尝试解释它们各自的用途。我假设您对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
不同的是,它采用一元函数。例如,在IO monad中,它的类型为(a -> IO b) -> [a] -> IO [b]
。它非常类似于普通的map
函数,只是它将一元操作应用于列表,并生成包装在一元操作中的结果列表。例如(一个非常糟糕的例子):
main2 :: IO ()
main2 = do
output <- mapM (putStrLn . show) [1, 2, 3]
putStrLn (show output)
这将打印:
1
2
3
[(),(),()]
它所做的是迭代列表,对列表中的每个元素应用(putStrLn . show)
(具有打印出每个数字的IO效果),并将数字转换为()
值。结果列表由putStrLn
的输出[(), (), ()]
组成。
发布于 2011-05-02 20:23:49
其他答案已经很好地解释了这一点,所以我只想指出,在实际的Haskell代码中,您通常会看到使用fmap
而不是liftM
,因为fmap
只是Functor
类型类中的一个更通用的版本。因为所有行为良好的Monad
都应该是Functor
的实例,所以它们应该是等价的。
您还可以看到运算符<$>
用作fmap
的同义词。
还有mapM f = sequence . map f
,因此您可以将其视为将值列表转换为操作列表,然后一个接一个地运行这些操作,将结果收集到一个列表中。
https://stackoverflow.com/questions/5856709
复制相似问题