Stdin作为IO句柄

时间:2010-08-15 16:59:38

标签: haskell

这可能是一个愚蠢的问题,但我无法在任何地方找到答案。我是Haskell新手,我遇到了I / O问题。

我有这个结构:

data SrcFile = SrcFile (IO Handle) String

srcFileHandle :: SrcFile -> IO Handle
srcFileHandle (SrcFile handle _) = handle

srcFileLine :: SrcFile -> String
srcFileLine (SrcFile _ string) = string

现在问题是我不知道如何将stdin / stderr / stdout分配到其中,因为stdin等是Handler,没有IO Handler。如果我使结构具有IO Handle的Handle属性,那么我将无法在其中添加任何其他文件句柄。

3 个答案:

答案 0 :(得分:6)

从您对SrcFile的定义来看,似乎您可能正在尝试在Haskell中编写C程序。语言shapes the way we think,好消息是Haskell是一种更强大的语言!

优秀的图书Real World Haskell有一个关于lazy I/O的部分。考虑摘录:

  

接近I / O的一种新方法是hGetContents功能。 hGetContents的类型为Handle -> IO String。它返回的String表示句柄给出的文件中的所有数据。

     

在严格评估的语言中,使用这样的函数通常是一个坏主意。读取2KB文件的全部内容可能没什么问题,但如果您尝试读取500GB文件的全部内容,则可能会因为缺少RAM来存储所有数据而崩溃。在这些语言中,传统上使用循环等机制来处理文件的整个数据。

这是激进的部分。

  

但是hGetContents是不同的。它返回的String被懒惰地评估。在您拨打hGetContents时,实际上没有任何内容被读取。仅在处理列表的元素(字符)时才从Handle读取数据。由于不再使用String的元素,Haskell的垃圾收集器会自动释放内存。所有这一切都完全透明地发生在你身上。既然你看起来像 - 而且真的是纯粹的String,你可以把它传递给纯(非IO)代码。

再往下是readFile and writeFile上的一个部分,它向您展示如何完全忘记句柄。

例如,假设您要从源文件中获取所有import行:

module Main where

import Control.Monad (liftM, mapM_)
import Data.List (isPrefixOf)
import System.Environment (getArgs, getProgName)
import System.IO (hPutStrLn, stderr)

main :: IO ()
main = getArgs >>= go
  where go [path] = collectImports `liftM` readFile path >>= mapM_ putStrLn
        go _ = getProgName >>=
               hPutStrLn stderr . ("Usage: " ++) . (++ " source-file")

collectImports :: String -> [String]
collectImports = filter ("import" `isPrefixOf`)
               . takeWhile (\l -> null l
                               || "module" `isPrefixOf` l
                               || "import" `isPrefixOf` l)
               . lines

即使main的定义使用readFile该程序只会根据需要读取指定的源文件,而不是全部内容!没有什么神奇之处:请注意collectImports使用takeWhile仅检查所需的行,而不是检查必须读取所有行的filter

当输入自己的源时,程序输出

import Control.Monad (liftM, mapM_)
import Data.List (isPrefixOf)
import System.Environment (getArgs, getProgName)
import System.IO (hPutStrLn, stderr)

所以拥抱懒惰。懒惰是你的朋友!享受Haskell的其余美妙旅程。

答案 1 :(得分:4)

我不确定您真正尝试做什么,但您可以使用Handle功能将IO Handle转换为return。所以,

stdin :: Handle
return stdin :: IO Handle

实际上,return是一个多态函数。它的类型为a -> m a,其中m可以是IOMaybe[]等。不要将它与C中的return混淆 - 它是一个普通函数,而不是用于过早退出的关键字。

在您的代码中,您可以使用记录语法。以下内容是等效的,并自动将srcFileHandlesrcFileLine声明为函数:

data SrcFile = SrcFile { srcFileHandle :: IO Handle,
                         srcFileLine :: String }

答案 2 :(得分:2)

我不太了解你想要实现的目标。

IO a表示:与外界的互动,在运行时会产生a

因此,在数据结构中存储IO Handle没有意义。您只需存储句柄,您就可以使用句柄执行IO ,但是为了存储/加载它,您不需要进行IO交互。

因此,您的结构是:

data SrcFile = SrcFile Handle String

如果要更改/添加/操作内容,可以使用IORef,它可以像 IO代码中的指针一样使用。