tags:

views:

137

answers:

3
+3  Q: 

Stdin as IO Handle

Hey guys,

this may be a stupid question but i couldn't find answer anywhere. I'm a Haskell newbie and i'm having trouble with I/O.

I have this structure:

data SrcFile = SrcFile (IO Handle) String

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

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

Now the problem is that i have no idea how to assign stdin/stderr/stdout into it, because the stdin etc are Handlers, no IO Handlers. And if i make the structure have Handle attributes insted of IO Handle, then i won't be able to add any other file handles into it.

Any help would be very appreciated. Regards, Tomas Herman

+2  A: 

I don't quite get what you're trying to achieve.

An IO a means: An interaction with the outside world that, when run, will yield an a.

It therefore doesn't make sense to store an IO Handle in a data structure. You just store the handle and you can do IO with the handle, but for storing/loading it, you have no IO interaction involved.

Hence your structure is:

data SrcFile = SrcFile Handle String

If you want to change/add/manipulate the contents, you can use an IORef which you can use like a pointer from IO code.

Dario
Not sure we want to introduce IORefs at this point. There is a time and a place for IORefs, but that time is not "Haskell 101 - Reading a file".
jrockway
+4  A: 

Judging from your definition of SrcFile, it seems as though you may be trying to write a C program in Haskell. Language shapes the way we think, and the good news is Haskell is a much more powerful language!

The excellent book Real World Haskell has a section on lazy I/O. Consider an excerpt:

One novel way to approach I/O is the hGetContents function. hGetContents has the type Handle -> IO String. The String it returns represents all of the data in the file given by the Handle.

In a strictly-evaluated language, using such a function is often a bad idea. It may be fine to read the entire contents of a 2KB file, but if you try to read the entire contents of a 500GB file, you are likely to crash due to lack of RAM to store all that data. In these languages, you would traditionally use mechanisms such as loops to process the file's entire data.

Here's the radical part.

But hGetContents is different. The String it returns is evaluated lazily. At the moment you call hGetContents, nothing is actually read. Data is only read from the Handle as the elements (characters) of the list are processed. As elements of the String are no longer used, Haskell's garbage collector automatically frees that memory. All of this happens completely transparently to you. And since you have what looks like—and, really, is—a pure String, you can pass it to pure (non-IO) code.

Further down is a section on readFile and writeFile that shows you how to forget about handles entirely.

For example, say you want to grab all the import lines from a source file:

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

Even though the definition of main uses readFile, the program reads only as much of the named source-file as necessary, not the whole thing! There's nothing magic going on: note that collectImports uses takeWhile to examine only those lines it needs to rather than, say, filter that would have to read all lines.

When fed its own source, the program outputs

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

So embrace laziness. Laziness is your friend! Enjoy the rest of the wonderful journey with Haskell.

Greg Bacon
Thanks for the answer! I am indeed trying to rewrite a lexical analyzer from c++ into Haskell. I should have probably just read a book first, instead of 'learning by doing' :]
Arg
So if i understand it correctly, if i want to read the entire file character by character, all i need to do is use hGetContents and then just analyze the first Char in the string it returns and Haskell will handle all IO even on large files. Is that about right?
Arg
@Arg You're welcome! Assuming you're not rewriting this lexer as a pure learning exercise, take a look at [alex](http://haskell.org/alex/), which is part of the [Haskell Platform](http://hackage.haskell.org/platform/contents.html).
Greg Bacon
Actually, i am :] We have this course on university about programing languages and compilers and the source codes for examples are available in c/c++ and i figured it would be cool to try to implement them in haskell. I will check out the link though, thanks
Arg
+3  A: 

I'm not sure what you're really attempting to do, but you can convert a Handle to IO Handle by using return function. So,

stdin :: Handle
return stdin :: IO Handle

In fact, return is a polymorphic function. It's type is a -> m a where m can be IO, Maybe, [] and others. Don't confuse it with return in C - it's a normal function, not a keyword that is used to exit prematurely.

In your code, you can use record syntax. The following is equivalent and automatically declares srcFileHandle and srcFileLine as functions:

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