本文介绍了Haskell预monadic I / O的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不知道在IO monad还没有发明的时候,在Haskell中I / O是如何完成的。任何人都知道一个例子。



编辑:如果没有现代Haskell中的IO Monad,我可以完成I / O吗?我更喜欢一个可以与现代GHC一起工作的例子。

解决方案

在引入IO monad之前, main 类型的函数[Response] - > [请求] 。一个 Request 代表一个I / O操作,如写入一个通道或文件,读取输入,或读取环境变量等。A Response 将是这种行为的结果。例如,如果执行了 ReadChan ReadFile 请求,则相应的 Response 将是 Str str 其中 str 应该是 String AppendChan AppendFile WriteFile 请求时,回应只会是成功。 (当然,假设给定的动作实际上是成功的)。

因此,一个Haskell程序可以通过建立请求值并从给出给 main 的列表中读取相应的响应。例如,从用户那里读取一个数字的程序可能如下所示(为简单起见,不考虑任何错误处理):

  main :: [Response]  - > [请求] 
主要响应=
[
AppendChanstdout请输入数字\\\

ReadChanstdin,
AppendChanstdout 。显示$ enteredNumber * 2
]
其中(Str input)=回复!! 1
firstLine = head。行$ input
enteredNumber =读取firstLine

正如Stephen Tetley在评论中指出的那样,该模型的详细规范在。






没有。 Haskell不再支持 Response / Request 直接执行IO的方式, main 现在是 IO(),所以你不能写一个不涉及 IO ,即使你可以,你仍然没有办法做任何I / O。



然而,你可以做的是写一个采用旧式主函数并将其转换为IO操作的函数。然后,您可以使用旧样式编写所有内容,然后仅在 main 中使用IO,您只需在真正的主函数上调用转换函数。这样做几乎肯定会比使用 IO monad更麻烦(并且会让任何现代Haskeller都难以理解),所以我绝对不会推荐它。然而它可能的。这样的转换函数可能如下所示:

  import System.IO.Unsafe 

- Since Request和Response类型不再存在,我们必须在这里重新定义
- 它们。为了支持更多的I / O操作,我们需要扩展
- 这些类型

data请求=
ReadChan字符串
| AppendChan字符串字符串

数据响应=
成功
| Str字符串
派生显示

- 使用IO monad执行请求并返回相应的响应。
executeRequest :: Request - > IO响应
executeRequest(AppendChanstdout消息)= do
putStr消息
返回成功
executeRequest(AppendChan chan _)=
错误(Output channel+ + chan ++not supported)
executeRequest(ReadChanstdin)= do
input< - getContents
return $ Str input
executeRequest(ReadChan chan)=
错误(输入通道++ chan ++不支持)

- 取一个旧式的main函数并将它变成一个IO动作
executeOldStyleMain ::( [响应] - > [请求]) - > IO()
executeOldStyleMain oldStyleMain = do
- 我真的很抱歉。
- 我认为不用unsafePerformIO
let respond = map(unsafePerformIO。executeRequest)就可以编写这个函数。 oldStyleMain $ response
- 确保所有响应都被评估(以便I / O实际上需要
- place),然后返回()
foldr seq(return())响应

然后你就可以使用这个函数了:

   - 在旧式Haskell应用程序中将数字加倍,这就是
- main函数
doubleUserInput :: [Response] - > ; [请求]
doubleUserInput响应=
[
AppendChanstdout请输入数字\\\

ReadChanstdin,
AppendChanstdout 。显示$ enteredNumber * 2
]
其中(Str input)=回复!! 1
firstLine = head。行$ input
enteredNumber =读取firstLine

main :: IO()
main = executeOldStyleMain doubleUserInput


I wonder how I/O were done in Haskell in the days when IO monad was still not invented. Anyone knows an example.

Edit: Can I/O be done without the IO Monad in modern Haskell? I'd prefer an example that works with modern GHC.

解决方案

Before the IO monad was introduced, main was a function of type [Response] -> [Request]. A Request would represent an I/O action like writing to a channel or a file, or reading input, or reading environment variables etc.. A Response would be the result of such an action. For example if you performed a ReadChan or ReadFile request, the corresponding Response would be Str str where str would be a String containing the read input. When performing an AppendChan, AppendFile or WriteFile request, the response would simply be Success. (Assuming, in all cases, that the given action was actually successful, of course).

So a Haskell program would work by building up a list of Request values and reading the corresponding responses from the list given to main. For example a program to read a number from the user might look like this (leaving out any error handling for simplicity's sake):

main :: [Response] -> [Request]
main responses =
  [
    AppendChan "stdout" "Please enter a Number\n",
    ReadChan "stdin",
    AppendChan "stdout" . show $ enteredNumber * 2
  ]
  where (Str input) = responses !! 1
        firstLine = head . lines $ input
        enteredNumber = read firstLine

As Stephen Tetley already pointed out in a comment, a detailed specification of this model is given in chapter 7 of the 1.2 Haskell Report.


No. Haskell no longer supports the Response/Request way of doing IO directly and the type of main is now IO (), so you can't write a Haskell program that doesn't involve IO and even if you could, you'd still have no alternative way of doing any I/O.

What you can do, however, is to write a function that takes an old-style main function and turns it into an IO action. You could then write everything using the old style and then only use IO in main where you'd simply invoke the conversion function on your real main function. Doing so would almost certainly be more cumbersome than using the IO monad (and would confuse the hell out of any modern Haskeller reading your code), so I definitely would not recommend it. However it is possible. Such a conversion function could look like this:

import System.IO.Unsafe

-- Since the Request and Response types no longer exist, we have to redefine
-- them here ourselves. To support more I/O operations, we'd need to expand
-- these types

data Request =
    ReadChan String
  | AppendChan String String

data Response =
    Success
  | Str String
  deriving Show

-- Execute a request using the IO monad and return the corresponding Response.
executeRequest :: Request -> IO Response
executeRequest (AppendChan "stdout" message) = do
  putStr message
  return Success
executeRequest (AppendChan chan _) =
  error ("Output channel " ++ chan ++ " not supported")
executeRequest (ReadChan "stdin") = do
  input <- getContents
  return $ Str input
executeRequest (ReadChan chan) =
  error ("Input channel " ++ chan ++ " not supported")

-- Take an old style main function and turn it into an IO action
executeOldStyleMain :: ([Response] -> [Request]) -> IO ()
executeOldStyleMain oldStyleMain = do
  -- I'm really sorry for this.
  -- I don't think it is possible to write this function without unsafePerformIO
  let responses = map (unsafePerformIO . executeRequest) . oldStyleMain $ responses
  -- Make sure that all responses are evaluated (so that the I/O actually takes
  -- place) and then return ()
  foldr seq (return ()) responses

You could then use this function like this:

-- In an old-style Haskell application to double a number, this would be the
-- main function
doubleUserInput :: [Response] -> [Request]
doubleUserInput responses =
  [
    AppendChan "stdout" "Please enter a Number\n",
    ReadChan "stdin",
    AppendChan "stdout" . show $ enteredNumber * 2
  ]
  where (Str input) = responses !! 1
        firstLine = head . lines $ input
        enteredNumber = read firstLine

main :: IO ()
main = executeOldStyleMain doubleUserInput

这篇关于Haskell预monadic I / O的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 23:06