2012-03-22 44 views
10

我認爲,原則上haskell的類型系統將禁止從純粹的函數中調用不純的函數(即f :: a -> IO b),但今天我意識到通過return來調用它們,它們編譯得很好。在例如:現在純函數中IO操作的含義是什麼?

h :: Maybe() 
h = do 
    return $ putStrLn "???" 
    return() 

h作品中,也許單子,但它是一個純函數不過。編譯並運行它只需返回Just(),而不需要實際執行任何I/O操作。我認爲哈斯克爾的懶惰把東西放在一起(即putStrLn的返回值沒有被使用 - 因爲它的值構造函數是隱藏的,我無法對它進行模式匹配),但爲什麼這個代碼是合法的呢?是否有任何其他原因使得這個允許?

作爲一個獎勵,相關問題:一般來說,是否有可能從其他內部禁止monad的所有操作?怎麼樣?

回答

19

IO操作是像任何其他操作一樣的一流值;這就是Haskell的IO如此表達的原因,讓您可以從頭構建更高階的控制結構(如mapM_)。懶惰在這裏不相關,這只是你實際上並不是執行的動作。你只是在構建價值Just (putStrLn "???"),然後把它扔掉。

putStrLn "???"現有不會將行打印到屏幕上。本身,putStrLn "???"只是一個描述的一些IO可以做到使一行被打印到屏幕上。發生的唯一執行是執行main,這是您從其他IO操作構建的,或者您鍵入GHCi的任何操作。有關更多信息,請參見introduction to IO

事實上,完全可以想象你可能想在Maybe內忙於約IO動作;設想一個函數String -> Maybe (IO()),它檢查字符串的有效性,如果有效,則返回IO操作以打印從字符串派生的一些信息。這可能恰恰是因爲Haskell的一流IO操作。

但是一個monad無法執行另一個monad的動作,除非你給它這個能力。

事實上,h = putStrLn "???" `seq` return()也不會導致任何IO執行,即使它強制評估putStrLn "???"

+0

如何讓monad從另一個執行動作的能力,通過賦予它與包含的值進行模式匹配的可能性? – 2012-03-22 09:46:17

+3

通過編寫將一個monad轉換爲另一個monad的方法,或者執行一些操作。例如,Control.Monad.ST.stToIO'將'ST'計算轉換爲'IO'計算。 – 2012-03-22 10:08:02

+0

晶瑩剔透。謝謝你們倆! – 2012-03-22 10:33:23

4

讓我們開始吧!現在

h = do return (putStrLn "???"); return() 
-- rewrite (do foo; bar) as (foo >> do bar) 
h = return (putStrLn "???") >> do return() 
-- redundant do 
h = return (putStrLn "???") >> return() 
-- return for Maybe = Just 
h = Just (putStrLn "???") >> Just() 
-- replace (foo >> bar) with its definition, (foo >>= (\_ -> bar)) 
h = Just (putStrLn "???") >>= (\_ -> Just()) 

,當你評估h會發生什麼?*嗯,也許吧,

(Just x) >>= f = f x 
Nothing >>= f = Nothing 

所以我們模式第一種情況相匹配

f x 
-- x = (putStrLn "???"), f = (\_ -> Just()) 
(\_ -> Just()) (putStrLn "???") 
-- apply the argument and ignore it 
Just() 

注意我們從來沒有執行putStrLn "???"爲了評估這個表達。

* n.b。有些不清楚「desugaring」停止和「評估」開始的時間點。這取決於您的編譯器內聯決策。純粹的計算可以在編譯時完全評估。

+1

感謝您的解職。對新人非常有用。我不明白爲什麼這麼多的教程開始與糖。首先有晚餐,然後沙漠。 – masterxilo 2017-03-14 21:14:35

相關問題