Frege/frege

add 'eachLine' to BufferedReader

Opened this issue · 2 comments

Dierk commented

Here is a suggestion to improve the usage of the BufferedReader that does not require to read the full content but allows line-by-line processing:

-- only for testing
data StringReader = native java.io.StringReader where
    native new      :: String -> STMutable s StringReader

-- convenience output
printIOValue prefix ioValue = do  -- same as:  (prefix ++) . show <$> ioValue >>= println
    value <- ioValue
    println $ prefix ++ show value


eachLine :: Mutable s BufferedReader → a → (a → String → ST s a) → ST s a
eachLine bufferedReader value handler = do
    line <- bufferedReader.readLine
    case line of
        Nothing   -> do
            bufferedReader.close
            return value
        Just line -> do
            newValue <- handler value line
            eachLine bufferedReader newValue handler

main = do
    testFile = File.new "TestFile.txt"              -- since version 3.25, otherwise <- instead of =

    -- delete test file if it already existed
    printIOValue "Test file deleted to clean up before start: " testFile.delete

    println "create test file"
    writeFile testFile.getPath $ unlines ["first line","second line","third line"]
    printIOValue "File now exists: " testFile.exists

    println "read test file in toto"
    content <- readFile testFile.getPath
    println "file content was:"
    println content

    println "append 2 lines"
    appendFile testFile.getPath $ unlines ["fourth line","fifth line"]

    println "processing each line, while keeping track of line numbers"
    bufferedReader <- openReader testFile.getPath
    count <- eachLine bufferedReader 0 $ \num line -> do
        println $ show (num + 1) ++ ": " ++ line
        return  $ num + 1
    println $ "total number of lines: " ++ show count

    println "pushing each line on a stack"
    bufferedReader <- openReader testFile.getPath
    stack <- eachLine bufferedReader [] $ \stack line -> return (line : stack)
    println $ "total stack" ++ show stack

    println "processing each line with a non-IO impure reader, here: StringReader. (great for testing)"
    stringReader <- StringReader.new $ unlines ["first","second","third"]
    bufStrReader <- BufferedReader.new stringReader
    result <- eachLine bufStrReader 0 $ \num _ -> return  $ num + 1
    println $ "processing strings with eachLine works as expected: " ++ show (result == 3)

    println "command line input (send EOF via Cmd/Ctrl-D)"
    eachLine stdin 0 $ \num line -> do
        println $ show (num + 1) ++ ": " ++ line
        return  $ num + 1

    return ()

Dierk commented

Hi, @Ingo60, please find my latest ideas under https://github.com/Dierk/fregeTutorial/blob/e3dc0b5e2dfc131c11dcd95be5ecd4a44eec6eef/src/main/frege/suggest/LineProducer.fr#L10
Suggestions for improvement highly welcome!

If you think this makes sense, then please give me a thumbs-up and I will add it to the stdlib with examples and test cases extra.

It requires the changes shown in line 51 and 54 (IOMutable to STMutable s for BufferedReader and FileInputStream).