Haskell program to extract the first words of each line of a text file isLineTerminator c = c == '\r' || c == '\n'
leadWords [] = []
leadWords cs =
    let (pre, suf) = break isLineTerminator cs
    in  (head (words pre)) : case suf of 
                            ('\r':'\n':rest) -&gt; leadWords rest
                            ('\r':rest)      -&gt; leadWords rest
                            ('\n':rest)      -&gt; leadWords rest
                            _                -&gt; []

fixLines :: String -&gt; String
fixLines input = unlines (leadWords input)

interactWith function inputFile outputFile = do
    input &lt;- readFile inputFile
    writeFile outputFile (function input)

main = mainWidth myFunction
  where mainWidth function = do
            args &lt;- getArgs
            case args of 
                [input, output] -&gt; interactWith function input output
                _               -&gt; putStrLn "error: exactly two arguments needed"
        myFunction = fixLines


Much of this program is written by the author of the book on Haskell that I'm reading, so I assume that much of it is good. However, my part is in altering the leadWords function. Does it look like a:


readable, elegant function?
efficient and optimized function?
Errors






The code block above is an example where your program will fail. Any empty line will lead to an empty list in words "" and therefore to head [], which calls error.

Standard library

That being said, there are some other issues with your code. You use unlines in fixLines, yet leadWords is essentially lines, although it also uses head . words internally. If we rewrite leadWords with map, we end up with

leadWords = map (head . words) . lines


which immediately shows the possible error on empty lines. Still, we can fix this if we write another function:

firstWord :: String -&gt; Maybe String
firstWord xs = case words xs of
                   (x : _) -&gt; Just x
                   _       -&gt; Nothing


Now we can use mapMaybe from Data.Maybe to write leadWords:

leadWords :: String -&gt; [String]
leadWords = mapMaybe firstWord . lines


Type signatures

Other than that, you should really add type signatures to your top-level functions. Only fixLines has one, and its not clear why that one got special handling.

Code complexity

main is overly complicated, too. We can just inline mainWidth, as its local binding doesn't yield any more comfort:

main :: IO ()
main =  do
    args &lt;- getArgs
    case args of 
        [input, output] -&gt; interactWith fixLines input output
        _               -&gt; putStrLn "error: exactly two arguments needed"


Alternatively, we can provide mainWith as top level function and use that in main. 

Complete code

If we apply all remarks given above, we end up with something similar to the following code:

import Data.Maybe (mapMaybe)

mainWith :: (String -&gt; String) -&gt; IO ()
mainWith f = do
    args &lt;- getArgs
    case args of 
        [input, output] -&gt; interactWith f input output
        _               -&gt; putStrLn "error: exactly two arguments needed"

fixLines :: String -&gt; String
fixLines = unlines . leadWords

main :: IO ()
main = mainWith fixLines

-- for completeness

leadWords :: String -&gt; [String]
leadWords = mapMaybe firstWord . lines

firstWord :: String -&gt; Maybe String
firstWord xs = case words xs of
                     (x : _) -&gt; Just x
                     _       -&gt; Nothing

-- That's just personal preference, your style is fine.
interactWith :: (String -&gt; String) -&gt; FilePath -&gt; FilePath -&gt; IO ()
interactWith f input output = readFile input &gt;&gt;= writeFile output . f


In short:


add type signatures to top-level functions
don't use head on lists that might be empty
use standard library types when possible (Maybe)
use standard library functions when possible (map, lines and mapMaybe)