了解Haskell中的liftM2

Sha*_*pta 2 io haskell

我很难理解liftM2haskell的工作原理。我编写了以下代码,但未输出任何内容。

import Control.Monad
main = liftM2 (\a b -> putStrLn$show$(+) a b) readLn readLn 
Run Code Online (Sandbox Code Playgroud)

che*_*ner 6

它有助于从以下类型开始liftM2

liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Run Code Online (Sandbox Code Playgroud)

第一个参数是2个参数的函数,例如(+)。通常,您将这样使用(+)

> 3 + 5
8
Run Code Online (Sandbox Code Playgroud)

但是,您没有两个type值Num a => a;您readLn :: Read a => IO a用来获取type的值Num a => IO a。如果尝试直接添加这些值:

:t (+) readLn readLn
(+) readLn readLn :: (Read a, Num (IO a)) => IO a
Run Code Online (Sandbox Code Playgroud)

它需要IO a有一个Num实例。也就是说,您不想添加readLn; 的返回值。您想添加包装在这些返回值中的数字。您可以明确地做到这一点:

do
 x <- readLn
 y <- readLn
 return $ x + y
Run Code Online (Sandbox Code Playgroud)

或者,您可以“修改” (+)以隐式解开参数,然后包装结果。就是liftM2这样:它接受一个2参数的函数,并将其“提升”到monad中,以便它可以处理包装的值。

> :t (+)
(+) :: Num a => a -> a -> a
> :t liftM2 (+)
liftM2 (+) :: (Num r, Monad m) => m r -> m r -> m r
Run Code Online (Sandbox Code Playgroud)

所以(+) 3 5 :: Num a => aliftM2 (+) $ (return 3) (return 5) :: (Monad m, Num a) => m a。前者评估为8,后者评估为return 8(对于return特定monad 执行任何操作)。一些非IO示例:

> (liftM2 (+)) (Just 3) (Just 5)
Just 8
> (liftM2 (+)) [3] [5]
[8]
> (liftM2 (+)) (Right 3) (Right 5)
Right 8
Run Code Online (Sandbox Code Playgroud)

liftM2与...非常相似map; 实际上,liftM(提升1参数功能的版本)只是的另一种名称map