我總是發現它有助於考慮簡化「勢在必行語言源代碼」實施IO
單子:
data IO :: * -> * where
PutStr :: String -> IO()
GetLine :: IO String
IOPure :: a -> IO a
IOSeq :: IO a -> IO b -> IO b
...
LaunchMissiles :: IO()
那麼什麼ioFunction
是很清楚的在數學意義上的正確,合理的功能:
ioFunction str = do putStrLn str
putStrLn "2"
= putStr (str++"\n") >> putStrLn "2\n"
= IOSeq (PutStr (str++"\n")) (PutStrLn "2\n")
這只是一個簡單的數據結構,它有效地表示了命令式語言的一些源代碼。 ioFunction
將給定的參數放置在結果結構中的特定位置,所以它在數學上非常重要,不僅僅是一個簡單的「返回()
並且沒有其他東西(可能發生的情況,但它是GHC實現細節)」。
的價值的確是ioFunction2
完全不同:
ioFunction2 str = do return()
= return()
= IOPure()
我們怎麼知道他們是在這個意義上有什麼不同?
這是一個很好的問題。實際上,你知道執行這兩個程序,並觀察它們會產生不同的效果,因此它們是must have been different。這當然有點尷尬 - 「觀察發生的事情」不是數學,而是物理學,觀察科學上要求在相同的環境條件下執行兩次。
糟糕的是,即便如此,一般認爲是數學上可以觀察到不同的行爲相同的純值:print 100000000
將立即引起副作用,而print $ last [0..100000000]
需要顯著暫停(其中,如果按照其與時間打印命令,實際上可以產生不同的文本輸出)。
但是這些問題在現實世界中是不可避免的。因此,Haskell's沒有指定IO上的任何結構,只有通過語言內部的數學嚴格性來檢查其是否相等,纔有意義。所以,在某種意義上你真的不能知道putStrLn str >> putStrLn "2"
是不一樣的return()
。
其實他們可能是一樣的。可以設想使一個甚至更簡單的玩具實施IO
比從而上述:
data IO :: * -> * where
IONop :: IO()
IOHang :: IO a -> IO a
和簡單地任何純輸出操作映射到無操作(和任何輸入到一個無限循環)。然後,你將有
ioFunction str = do putStrLn str
putStrLn "2"
= IONop >> IONop
= IONop
和
ioFunction2 str = return()
= IONop
這一點,就好像我們已經強加於自然數的附加公理,即1 = 0。從這你就可以得出結論:每號碼爲零。
顯然,這將是一個完全無用的實現。但是,如果您在沙箱環境中運行Haskell代碼,並且希望完全確定沒有發生任何錯誤,那麼類似的東西實際上可能有意義。 http://tryhaskell.org/做這樣的事情。
這意味着你有點困惑。 Monad可以用來舉例說明一個*隱藏*狀態,並確實在執行中強制執行。 IO monad在我看來與國家monad有許多共同之處,只不過它隱藏了操作系統固有的各種不確定行爲。 –
我在說的是:從GHC的角度來看,ioFunction首先打印'str',然後打印「2」。然而從數學的角度來看,這個函數只需要一個'String'並返回'():: IO()'。因此,我們碰巧在Haskell中使用IO monad進行副作用的事實在某種意義上是任意的,並且與(數學)monad(IO類型)的內在意義無關。 – George
否函數不返回'()',它返回一個IO monad(這是一個函數),monad最終返回'()'。這是不同的。事實上IO有一些* intrensic *的數學意義。 –