2017-05-04 60 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