天天看點

haskell的世界觀(3)

為什麼monad的引入就能夠把pure world和real world和諧的結合起來呢? rollDice函數不是不符合“給出相同的參數,傳回相同的結果”麼?   我們先來看看pure function的定義吧: wikipedia上是這麼寫的:

In computer programming, a function may be described as pure if both these statements about the function hold.

  1. The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change as program execution proceeds, nor can it depend on any external input from IO devices.
  2. Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to IO devices.

翻譯一下就是: 1. 給出相同的參數,函數總是求值出相同的結果。結果不能依賴于任何在程式運作過程中可能改變的隐含的資訊或者狀态,也不能依賴于任何外部輸入例如IO裝置。 2. 對結果的求值不能導緻任何語義上可以觀察到的副作用或者輸出,例如可變對象的改變或者IO裝置上的輸出。   結論是顯然的但又是令人困惑的,rollDice不是pure的?! 但是haskell作為一個pure functional語言是不允許定義impure的function的。 沖突!?   其實,因為有了monad和lazy evaluation,這個沖突便得以調和。 還記得rollDice的類型吧:rollDice :: IO Integer//rand(1,6) 你說它是一個monad也好,說它是一個制造monad的函數也好,請記住,函數是一類公民! 好,注意這裡,我們并不需要rollDice的結果,因為現在用不到。   然後,我們把它放到了一個mapM裡面去運算: mapM (/x->rollDice) [1..12] 用rollDice構造出來的函數(/x->rollDice)是:t->IO Integer//rand(1,6)   看看mapM是幹什麼的吧: mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

好啦,mapM由[1..12]得到了一個新的monad IO [Integer]//rand(1,6) repeat 12 我們還是不需要求值。   現在,該把這個monad從pure world扔到real world了,我們使用print :: a -> IO () (mapM (/x->rollDice) [1..12])>>=print monad IO [Integer]把它的[Integer]部分交給了print,bind之後得到了一個新的monad IO ()//print rand(1,6) repeat 12 to stdout   real world不是lazy的,它計算了12次rand(1,6),然後把它們列印到了終端上,于是我們的終端上立刻顯示出了諸如[6,6,2,6,5,3,1,3,6,4,2,5]之類的數列。   回過頭來看一看,rollDice、mapM、print之類的函數是pure function嗎? 答案是——yes!   在pure world,函數是lazy的。rollDice每次調用都會傳回IO rand(1,6),在純函數世界裡,這不過就是一個名字"rand(1,6)"而已;而這個monad卻同時定義了真實世界裡的一個計算,那就是計算1~6之間的一個随機數。當把這個monad從pure world扔到real world之後,real world便進行強制求值,于是就得到了一個随機數。   lazy要call by name,計算name,傳回name。strict要call by value,計算value,傳回value。這就是haskell的純函數世界和真實世界最大的不同。   [email protected]原創,轉貼請注明出處,謝謝!