Line Filters in Haskell

Here’s the translation of the Go line filter program to Haskell, with explanations in Markdown format suitable for Hugo:

import System.IO
import Data.Char (toUpper)

main :: IO ()
main = do
    -- We use 'getContents' to read from stdin, which is lazy and will read
    -- input line by line as needed.
    contents <- getContents
    
    -- Map over each line, convert to uppercase, and print
    mapM_ (putStrLn . map toUpper) (lines contents)

A line filter is a common type of program that reads input on stdin, processes it, and then prints some derived result to stdout. grep and sed are common line filters.

Here’s an example line filter in Haskell that writes a capitalized version of all input text. You can use this pattern to write your own Haskell line filters.

The program uses getContents to lazily read from stdin. This means it will process input line by line as it becomes available, rather than reading all input at once.

We then use lines to split the input into a list of strings, one for each line. We map over this list with a function that converts each line to uppercase using map toUpper.

Finally, mapM_ is used to apply putStrLn to each uppercase line, printing it to stdout.

To try out our line filter, first make a file with a few lowercase lines.

$ echo 'hello'   > /tmp/lines
$ echo 'filter' >> /tmp/lines

Then use the line filter to get uppercase lines.

$ cat /tmp/lines | runhaskell line-filters.hs
HELLO
FILTER

In this Haskell version, we don’t need to explicitly handle errors as we did in the Go version. Haskell’s IO operations will throw exceptions if something goes wrong, which will terminate the program unless caught.

The Haskell version is more concise and uses the language’s built-in laziness to process the input efficiently, line by line, without needing to manually manage a scanner or buffer.