robb.re

Haskell and stdin

Posted on July 28, 2015

I was writing a small command line application in Haskell and came across an annoying issue. I wanted to write a command that works similarly to cat where you can pipe stdin to the command like this

tail somefile.txt | fooapp

or operate directly on a file like this

fooapp somefile.txt

The issue that cropped up for me was that using getLine or getContents blocked while waiting for stdin. One option of course is to only try to read from stdin when no arguments are passed but my preference in that case would be to print usage instructions.

The solution I used was something like this (the types likely don’t match on this edited version):

import System.IO (hIsTerminalDevice, stdin)

main = do
    -- when receiving piped data then hIsTerminalDevice returns False
    s <- hIsTerminalDevice stdin
    x <- if s then return Nothing else
        (do contents <- getContents
            return $ Just contents)
    a <- case x of
            Nothing -> do
                ar <- getArgs
                return $ maybe Nothing listToMaybe ar
            Just t  -> return $ Just [t]