The language-puppet library has been created when I started to learn Haskell. As a consequence, it uses the dreaded String type to store all kind of textual values. It also uses the System.IO module for performing I/O. I was aware of the file descriptor leak problem that happens when you use readFile , so I chose for the following implementation for the Puppet file function:

1 2 3 file :: [ String ] -> IO ( Maybe String ) file [] = return Nothing file ( x : xs ) = catch ( fmap Just ( withFile x ReadMode hGetContents )) ( \ _ -> file xs )

This should return Just the content of the first readable file in the parameter list, or Nothing if there are none, and should not leak any file descriptor. Now that I am finalizing the hspuppetmaster binary, I can use my library to (try to) compute catalogs on my production systems, using the standard puppet agent -t --noop . It turned out that the file function was misbehaving. Testing it in GHCi illustrates the problem:

1 2 3 4 > file [ "/nothing" ] Nothing > file [ "/nothing" , "/etc/hosts" ] Just ""

It seems to work fine, except all file contents are empty. This behavior seems to be common knowledge among Haskellers, and is due to the fact that the file descriptor is closed before the output is evaluated. This is pretty horrible (and surprising), and what is even worse is my solution:

1 2 3 4 5 6 file ( x : xs ) = catch ( fmap Just ( withFile x ReadMode ( \ fh -> do { y <- hGetContents fh ; evaluate ( length y ) ; return y }))) (( \ _ -> file xs ) :: SomeException -> IO ( Maybe String ))