了解haskell中的liftM2

时间:2017-05-04 11:27:51

标签: haskell io

我很难理解liftM2在haskell中是如何运作的。 我编写了以下代码,但它没有输出任何内容。

import Control.Monad
main = liftM2 (\a b -> putStrLn$show$(+) a b) readLn readLn 

2 个答案:

答案 0 :(得分:5)

liftM2

的类型开头是有帮助的
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r

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

> 3 + 5
8

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

:t (+) readLn readLn
(+) readLn readLn :: (Read a, Num (IO a)) => IO a

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

do
 x <- readLn
 y <- readLn
 return $ x + y

或者,你可以修改&#34; (+)隐式解包参数,然后包装结果。这是liftM2的作用:它需要一个2参数函数,并且#34;提升&#34;它进入monad所以它可以处理包裹的值。

> :t (+)
(+) :: Num a => a -> a -> a
> :t liftM2 (+)
liftM2 (+) :: (Num r, Monad m) => m r -> m r -> m r

同时(+) 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

liftM2map非常相似;事实上,liftM(提升1参数函数的版本)只是map的另一个名称。

答案 1 :(得分:3)

我不认为编译器可以在$周围没有空格的情况下解析它。然后,main将具有类型IO(IO())

如果你想总结&#34;内部&#34; IO,您可以使用liftM2 (+),然后打印结果。

例如:

main :: IO ()
main = print =<< liftM2 (+) readLn readLn

或者使用do notation:

main :: IO ()
main = do
  s <- liftM2 (+) readLn readLn
  print s