Haskell前monadic I/O.

Tem*_*ora 49 io monads haskell

我想知道在IO monad尚未发明的时代,Haskell中的I/O是如何完成的.任何人都知道一个例子

编辑:现代Haskell中没有IO M​​onad可以完成I/O吗?我更喜欢一个适用于现代GHC的例子.

sep*_*p2k 60

在介绍IO monad之前,它main是一个类型的函数[Response] -> [Request].A Request表示I/O操作,如写入通道或文件,或读取输入,或读取环境变量等.A Response将是此类操作的结果.例如,如果你执行一个ReadChanReadFile请求时,相应的ResponseStr str其中str将一个String含有该读取的输入.当执行AppendChan,AppendFileWriteFile请求,响应将仅仅是Success.(假设在所有情况下,当然,给定的行动实际上是成功的).

因此,Haskell程序可以通过构建Request值列表并从给出的列表中读取相应的响应来工作main.例如,从用户读取数字的程序可能看起来像这样(为简单起见,省略任何错误处理):

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 
Run Code Online (Sandbox Code Playgroud)

正如Stephen Tetley在评论中已经指出的那样,该模型的详细说明在1.2 Haskell报告的第7章中给出.


现代Haskell中没有IO M​​onad可以完成I/O吗?

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

但是,你可以做的是编写一个函数,它接受旧式的main函数并将其转换为IO动作.然后,您可以使用旧样式编写所有内容,然后仅使用IO main,您只需在真正的主函数上调用转换函数.这样做几乎IO肯定会比使用monad 更麻烦(并且会让任何现代Haskeller阅读你的代码感到困惑),所以我绝对不会推荐它.但是可能.这样的转换函数可能如下所示:

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
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用这个函数:

-- 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
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用unsafeInterleaveIO而不是unsafePerformIO.那将是旧Haskell曾经做过的懒惰I/O模型.你也许可以通过使用io-streams,管道或管道给它一个现代旋转. (8认同)
  • unsafeInterleaveIO绝对是实现此目的的方法。 (2认同)