2017-05-04 61 views
1

我很难理解liftM2在haskell中的工作原理。 我写了下面的代码,但它不输出任何东西。了解haskell中的liftM2

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

回答

3

我不认为编译器可以解析这个没有空格$左右。然后,这里主要有类型IO(IO())

如果你想总结“内部”IO,你可以使用liftM2 (+),然后打印结果。

例如:

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

或者用做记号:

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

好的,我们如何使用'liftM2'来读取两个整数并打印它们的总和? –

+0

只是编辑我的答案添加此。 –

+2

当'-XTemplateHaskell'处于活动状态时,不带空格的'$'只是一个问题。但是,可能总是在它周围留出空间是一个好主意,这样,如果您以后需要使用TH,则不必担心它。无论如何,'$'是一个非常“太空”的操作符,它的优先级低。 – leftaroundabout

4

它有助于先从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 aNum实例。也就是说,你不想添加readLn的返回值;你想在这些返回值中添加包装的。你能做到这一点明确:

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

或者,你可以“修改” (+)隐含解开的参数,然后再包的结果。这就是liftM2所做的:它需要一个2参数函数,并将它“提升”到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对特定单子做什么)。一些非IO例子:

> (liftM2 (+)) (Just 3) (Just 5) 
Just 8 
> (liftM2 (+)) [3] [5] 
[8] 
> (liftM2 (+)) (Right 3) (Right 5) 
Right 8 

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

+0

虽然我想我可以理解你想解释的内容,但我认为你的'liftM2(+)$(IO 3)(IO 5)== IO 8'可能会引起误导。 –

+0

什么,只是因为'IO 3'和'IO 5'实际上并不是有效的Haskell表达式? :) – chepner

+0

我认为我的改变是一种改进。 – chepner