簡體   English   中英

Haskell中的IO類型是什么

[英]What is the IO type in Haskell

我是 Haskell 編程語言的新手,我一直在IO類型上絆倒,無論是作為 function 參數還是返回類型。

playGame :: Screen -> IO ()

或者

gameRunner :: IO String -> (String -> IO ()) -> Screen -> IO ()

這是如何工作的,我有點困惑,因為我知道 String 需要單詞而 Int 需要數字。 函數期望或返回中使用的IO是什么?

IO是 Haskell 如何區分引用透明代碼和不透明代碼的方式。 IO a是返回a的 IO 操作的類型。

您可以將 IO 操作視為一段對等待執行的現實世界有一定影響的代碼。 由於這種副作用,IO 操作不是引用透明的; 因此,執行順序很重要。 正確排序和執行所有 IO 動作是 Haskell 程序的main function 的任務。 因此,當您編寫返回IO a的 function 時,您實際上正在做的是編寫 function 執行最終返回的動作 - 當由main執行並返回a動作時。

更多解釋:

引用透明性意味着您可以將 function 替換為其值。 一個參照透明的 function 不會有任何副作用; 特別是,引用透明的 function 無法訪問任何硬件資源,如文件、網絡或鍵盤,因為 function 值將取決於其參數以外的其他內容。

像 Haskell 這樣的函數式語言中的引用透明函數就像數學函數(域和余域之間的映射),而不僅僅是關於如何計算函數值的命令式指令序列。 Therefore, Haskell code says the compiler that a function is applied to its arguments, but it does not say that a function is called and thus actually computed.

因此,引用透明函數並不意味着執行順序。 Haskell 編譯器可以自由地以它認為合適的任何方式評估函數 - 或者如果沒有必要則根本不評估它們(稱為惰性評估)。 唯一的排序來自數據依賴性,當一個 function 需要另一個 function 的 output 作為輸入時。

現實世界的副作用不是引用透明的。 您可以將現實世界視為某種隱含的全局 state ,有效函數會發生變異。 由於這個 state,執行順序很重要:如果您首先從數據庫中讀取然后更新它會有所不同,反之亦然。

Haskell 是一種純函數式語言,它的所有函數都是引用透明的,編譯依賴於此保證。 那么,我們如何處理有效的函數,這些函數操縱一些全局真實世界 state 並且需要按特定順序執行? 通過在這些函數之間引入數據依賴性。

This is exactly what IO does: Under the hood, the IO type wraps an effectful function together with a dummy state paramter. 每個 IO 操作都將此虛擬 state 作為輸入,並將其作為 output 提供。 Passing this dummy state parameter from one IO action to the next creates a data dependency and thus tells the Haskell compiler how to properly sequence all the IO actions.

您看不到虛擬 state 參數,因為它隱藏在一些語法糖后面: main和其他 IO 操作中的do表示法,以及IO類型內部。

簡單地說:

f1 :: A -> B -> C

是一個 function ,它采用AB類型的兩個 arguments 並返回一個C 它不執行任何 IO。

f2 :: A -> B -> IO C

f1類似,但也可以執行 IO。

f3 :: (A -> B) -> IO C

將 function A -> B (不執行 IO)作為參數並生成C ,可能執行 IO。

f4 :: (A -> IO B) -> IO C

takes as an argument a function A -> IO B (which can perform IO) and produces a C , possibly performing IO.

f5 :: A -> IO B -> IO C

takes as an argument a value of type A , an IO action of type IO B , and returns a value of type C , possibly performing IO (eg by running the IO action argument one or more times).

例子:

f6 :: IO Int -> IO Int
f6 action = do
   x1 <- action
   x2 <- action
   putStrLn "hello!"
   x3 <- action
   return (x1+x2+x3)

當 function 返回IO ()時,它沒有返回有用的值,但可以執行 IO。 類似於在 C 或 Java 中返回void 您的

gameRunner :: IO String -> (String -> IO ()) -> Screen -> IO ()

function 可以用下面的 arguments 調用:

arg1 :: IO String
arg1 = do
   putStrLn "hello"
   s <- readLine
   return ("here: " ++ s)

arg2 :: String -> IO ()
arg2 str = do
   putStrLn "hello"
   putStrLn str
   putStrLn "hello again"

arg3 :: Screen
arg3 = ... -- I don't know what's a Screen in your context

讓我們先嘗試回答一些簡單的問題:

  • Haskell 中的Maybe類型是什么?

    來自Haskell 2010 報告的第 21 章(第 205 頁):

     data Maybe a = Nothing | Just a

    這是一個簡單的部分類型 - 你有一個值(通過Just傳達)或者你沒有( Nothing )。

  • 這是如何運作的?

    讓我們看一下Maybe的一個可能的Monad實例:

     instance Monad Maybe where return = Just Just x >>= k = kx Nothing >>= _ = Nothing

    這個 monadic 接口簡化了基於Maybe構造函數的值的使用,例如,而不是:

     \f ox oy -> case ox of Nothing -> Nothing Just x -> case oy of Nothing -> Nothing Just y -> Just (fxy)

    你可以簡單地寫這個:

     \f ox oy -> ox >>= \x -> oy >>= \y -> return (fxy)

    monadic 接口應用廣泛:從解析到封裝 state 等等。

  • 函數中使用的Maybe類型期望或返回什么?

    對於 function 期望基於Maybe的值,例如:

     maybe:: b -> (a -> b) -> Maybe a -> b maybe _ f (Just x) = fx maybe d _ Nothing = d

    如果它的內容在 function 中使用,那么 function 可能不得不處理沒有接收到它可以使用的值,即Nothing

    對於 function 返回基於Maybe的值,例如:

     invert:: Double -> Maybe Double invert 0.0 = Nothing invert d = Just (1/d)

    它只需要使用適當的構造函數。

    最后一點:觀察如何使用基於Maybe的值 - 從簡單開始(例如invert 0.5Just "here" )然后定義其他可能更精細的基於Maybe的值(使用(>>=)(>>)等)最終通過模式匹配直接檢查,或通過適當的定義抽象地檢查( maybefromJust等人)。


原始問題的時間:

  • Haskell 中的IO類型是什么?

    來自報告的第 6.1.7 節(第 75 頁):

    IO類型用作與外界交互的操作(動作)的標記。 IO類型是抽象的:沒有構造函數對用戶可見。 IOMonadFunctor類的一個實例。

    關鍵是:

    IO類型是抽象的:沒有構造函數對用戶可見。

    沒有構造函數? 這就引出了下一個問題:

  • 這是如何運作的?

    這就是單子接口的多功能性所在:它的兩個關鍵操作符的靈活性——Haskell 中的return(>>=) ——基本上彌補了基於IO的抽象值。

    還記得關於如何使用基於Maybe的值的觀察嗎? 那么,基於IO的值以類似的方式使用 - 簡單地開始(例如return 1getCharputStrLn "Hello, there!" )定義其他基於IO的值(使用(>>=)(>>)catch等)最終形成Main.main

    但不是模式匹配或調用另一個 function 來提取其內容, Main.main直接由 Haskell 實現處理。

  • 函數中使用的IO期望或返回什么?

    對於 function 期望基於IO的值,例如:

     echo:: IO () echo:: getChar >>= \c -> if c == '\n' then return () else putChar c >> echo

    如果它的內容在 function 中使用,那么 function 通常會返回基於IO的值。

    對於 function 返回基於IO的值,例如:

     newLine:: IO () newLine = putChar '\n'

    它只需要使用適當的定義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM