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.