Fun with Haskell

What better way is there to spend a Saturday than playing with Haskell? I sure don’t know :-)

I do have a goal in mind, but for now I’m mostly fooling around with some file-related stuff.

First, I wanted to wanted to load an entire file and print it on stdout. Basically a primitive cat. I came up with the following main:

main = liftM (!! 0) getArgs >>= catFile >>= mapM_ putStrLn

As you can see I did spend some time thinking about Monads lately. Something that’s especially pressing if one wants to do IO in Haskell since the IO Monad can’t be escaped from. Next catFile (which based on the use above should have the type FilePath -> IO [String]). I did some experimenting and searching in Hoogle among the System.IO functions. I found openFile, hGetContents that would let me open and read the file. I played a bit with that:

catFile fname = do
    hf <- openFile fname ReadMode
    conts <- hGetContents hf
    hClose hf
    return conts

That doesn’t have quite the required type, but that isn’t the only problem. The combination of hGetContents and hClose doesn’t quite work—the file is never read (laziness in action!). I removed the hClose (since the process isn’t very long-lived that doesn’t bother me so much) and I added a lines:

catFile fname = do
    hf <- openFile fname ReadMode
    conts <- hGetContents hf
    return $ lines conts

Then I stumbled on readFile and rewrote it as

catFile fname = do
    content <- readFile fname
    return $ lines content

which can be written even shorter as

catFile fname = readFile fname >>= return . lines

I guess it’s time to explain the reason for returning IO [String] rather than IO String. The latter would have simplified both catFile and main. What I really wanted was to make each line of a file an item in a set. My first thought was to fold it:

catFile "test_file" >>= (\l -> return $ foldr Set.insert Set.empty l)

Which can be re-written in point-free style (I hope I’ve gotten point-free right here):

catFile "test_file" >>= return . foldr Set.insert Set.empty

Then I decided to look into Data.Set and found fromList. My first naïve attempt and my point-free:

catFile "test_file" >>= (\l -> return $ Set.fromList l)
catFile "test_file" >>= return . Set.fromList

Then I reversed my thinking and tried to lift the function into the monad instead:

liftM Set.fromList $ catFile "test_file"

At that point I felt I had somewhat exhausted this particular little playground and went on to listing files and directories, but that’s a story for another post.

Leave a comment