繁体   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