Update: I/O support in more recent versions of Frege
As of version 3.21.80, we have better I/O support in the standard libraries:
- The runtime provides
stdout
and stderr
(buffered, UTF8 encoding java.io.PrintWriters
wrapped around java.lang.System.out
and java.lang.System.err
) and stdin
(UTF8 decoding java.io.BufferedReader
wrapped around java.lang.System.in
)
- Functions
print
, println
, putStr
, putChar
write to stdout
getChar
and getLine
read from stdin
and throw exceptions on end of file.
- The Frege equivalents for Java classes like
PrintWriter
, BufferedWriter
etc. are defined in module Java.IO
, which is automatically imported. With this, more basic functionality is supported. For example, BufferedReader.readLine
has a return type of IO (Maybe String)
and does signal the end of file by returning Nothing
, like its Java counterpart, who returns null
in such cases.
Here is a short example program that implements a basic grep:
--- A simple grep
module examples.Grep where
--- exception thrown when an invalid regular expression is compiled
data PatternSyntax = native java.util.regex.PatternSyntaxException
derive Exceptional PatternSyntax
main [] = stderr.println "Usage: java examples.Grep regex [files ...]"
main (pat:xs) = do
rgx <- return (regforce pat)
case xs of
[] -> grepit rgx stdin
fs -> mapM_ (run rgx) fs
`catch` badpat where
badpat :: PatternSyntax -> IO ()
badpat pse = do
stderr.println "The regex is not valid."
stderr.println pse.getMessage
run regex file = do
rdr <- utf8Reader file
grepit regex rdr
`catch` fnf where
fnf :: FileNotFoundException -> IO ()
fnf _ = stderr.println ("Could not read " ++ file)
grepit :: Regex -> BufferedReader -> IO ()
grepit pat rdr = loop `catch` eof `finally` rdr.close
where
eof :: EOFException -> IO ()
eof _ = return ()
loop = do
line <- rdr.getLine
when (line ~ pat) (println line)
loop
Because Frege is still quite new, the library support is admittedly still lacking, despite the progress that is already done in the most basic areas, like Lists and Monads.
In addition, while the intent is to have a high degree of compatibility to Haskell, especially in the IO system and generally in the low level system related topics, there is a tension: Should we rather go the Java way or should we really try to emulate Haskell's way (which is in turn obviously influenced by what is available in the standard C/POSIX libraries).
Anyway, the IO thing is probably the most underdeveloped area of the Frege library, unfortunately. This is also because it is relatively easy to quickly write native function declarations for a handful of Java methods one would need in an ad hoc manner, instead of taking the time to develop a well though out library.
Also, a Read class does not exist up to now. As a substiutute until this has been fixed, the String type has functions to parse all number types (based on the Java parseXXX() methods).
(Side note: Because my days also have only 24h and I have a family, a dog and a job to care about, I would be very happy to have more contributors that help making the Frege system better.)
Regarding your code: Yes, I feel it is right to do all character based I/O through the Reader and Writer interfaces. Your example shows also that convenience functions for obtaining a standard input reader are needed. The same holds for standard output writer.
However, when you would need to read more than 1 line, I'd definitly create the reader in the main function and pass it to the input processing actions.