我很难理解liftM2
在haskell中是如何运作的。
我编写了以下代码,但它没有输出任何内容。
import Control.Monad
main = liftM2 (\a b -> putStrLn$show$(+) a b) readLn readLn
答案 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 => a
,liftM2 (+) $ (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
liftM2
与map
非常相似;事实上,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