Haskell中的IO Action
參考資料:
r/haskell: what is exactly side effect?
Haskell Wiki: Intro to Haskell IO/Actions
在Haskell中,一個interactive program被視為一個「以目前世界狀態為input、以改變後的世界狀態為output」的純函數(pure function),新的世界狀態反映了在程式執行過程中所產生的副作用(side effect)。
問題:副作用是什麼?如何定義?
假設有個type World
用來反映世界的狀態,那麼一個interactive program便可用類別為 World -> World
的函數來表達,並簡稱為IO
:
1 | type IO = World -> World |
以使用者在鍵盤上敲敲打打的事件為例,可以把使用者敲打鍵盤之前的世界狀態視為原本的世界狀態,把使用者敲打鍵盤之後的世界狀態視為後來的世界狀態。也就是說,「敲打鍵盤」這個副作用改變了世界的狀態。
但一個interactive program除了副作用之外,我們也有可能希望能回傳值,以供後續使用。像是「使用者在鍵盤上輸入文字」這個副作用,在敲鍵盤之外,我們還希望可以從他的敲鍵盤的行為讀取(萃取?)到他所輸入的值,這個值之後可以用來繼續丟到下一個用來登入系統的函數。因此有必要重新修改一下上面對type IO
的定義:
1 | type IO a = World -> (a, World) |
例如:
- 「從user的鍵盤輸入而讀取來的一個字元」具有
IO Char
的類型。這個字元的類型之所以不只是Char
,是因為要強調它是一個透過副作用(aka IO action)才得來的字元 - 打印一個字串在螢幕上具有
IO ()
的類型,因為將一個字串打印在螢幕上,只是純粹改變螢幕的狀態,所以回傳一個()
代表回傳空值。()
稱為unit。
一個具有IO
類型的表達式(expression)叫做一個IO action。
IO action的幾個範例:
- 將字串”hello”印在console上: 具有
IO ()
類型- 從console中讀取一行input: 具有
IO String
類型- 建立一個網路連線連到 www.google.com 的 port 80: 具有
IO Socket
類型- 從terminal中讀取兩行輸入,並將兩行的輸入讀取成兩個數字,將兩個數字相加後,印在螢幕上: 具有
IO Int
類型- 一個以滑鼠動作為輸入、並將一格格的圖像顯示在螢幕上的第一人稱射擊遊戲: 具有
IO ()
類型
必須記住的是,一個IO action,由於本身就是一個expression,在Haskell中和Int
或Char
一樣被當成一個值(value)來看待,既可以綁定到變數名稱上(使用let
或where
)、也可以作為函數的參數、更可以作為函數的回傳值。
最後這一點(將IO action
作為函數的回傳值)使我們可以細緻區分putStrLn
函數與該函數的回傳值之間的區別。
1 | putStrLn :: String -> IO () |
putStrLn
是一個以String
為參數並回傳函數,