Frege's equivalent of Haskell's getLine and read
-
13-11-2019 - |
Question
Is there any Frege's equivalent of Haskell's getLine
and read
to parse input from the console in the standard library?
Currently I am doing it like this:
import frege.IO
getLine :: IO String
getLine = do
isin <- stdin
isrin <- IO.InputStreamReader.new isin
brin <- IO.BufferedReader.fromISR isrin
line <- brin.readLine
return $ fromExceptionMaybe line
fromExceptionMaybe :: Exception (Maybe a) -> a
fromExceptionMaybe (Right (Just r)) = r
fromExceptionMaybe (Right _) = error "Parse error on input"
fromExceptionMaybe (Left l) = error l.getMessage
pure native parseInt java.lang.Integer.parseInt :: String -> Int
main _ = do
line <- getLine
println $ parseInt line
Update:
Frege has been evolved so now we have getLine
in the standard library itself. As for read
, we have conversion methods on String. Now the original problem is simply,
main _ = do
line <- getLine
println line.atoi
See Ingo's answer below for more details.
Solution
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
andstderr
(buffered, UTF8 encodingjava.io.PrintWriters
wrapped aroundjava.lang.System.out
andjava.lang.System.err
) andstdin
(UTF8 decodingjava.io.BufferedReader
wrapped aroundjava.lang.System.in
) - Functions
print
,println
,putStr
,putChar
write tostdout
getChar
andgetLine
read fromstdin
and throw exceptions on end of file.- The Frege equivalents for Java classes like
PrintWriter
,BufferedWriter
etc. are defined in moduleJava.IO
, which is automatically imported. With this, more basic functionality is supported. For example,BufferedReader.readLine
has a return type ofIO (Maybe String)
and does signal the end of file by returningNothing
, like its Java counterpart, who returnsnull
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.