Search code examples
haskellio-monad

Getting a first element of the list within IO monad


I'm trying to get the first element from the chain of IO monads (as far as I understand, I'm still learning Haskell):

getSomePath :: FilePath -> IO [FilePath]
getSomePath path = do
    pomFile <- listDirectory path >>= filterM (\x -> return $ x == "pom.xml") >>= head
    -- here I'll add some more files 
    return [pomFile, someOtherFile]

The error appears when I'm trying to apply head function:

Couldn't match type ‘[Char]’ with ‘IO FilePath’
      Expected type: [[Char]] -> IO FilePath
        Actual type: [IO FilePath] -> IO FilePath
    • In the second argument of ‘(>>=)’, namely ‘head’
      In a stmt of a 'do' block:
        pomFile <- listDirectory path
                     >>= filterM (\ x -> return $ x == "pom.xml")
                     >>= head

I've also tried:

pomFile <- head (listDirectory path >>= filterM (\x -> return $ x == "pom.xml"))

with no result. How should I apply the head function to the IO [a] type so that it results with IO a? Is there a head equivalent for applying it to monads? I know I could just use System.Directory.findFile or something similar but I wanted to understand why my approach doesn't work.


Solution

  • You can not use head, since the argument is here an IO [a], not an a. You can however fmap :: Function f => (a -> b) -> f a -> f b on this, and thus obtain an IO a, or use the operator alias <$>.

    That being said, using a filterM is here not necessary, you can just filter the list, and then take the head:

        pomFile <- (head . filter ("pom.xml" ==)) <$> listDirectory path

    The above is however not safe. If the directory does contain a "pom.xml", then it will simply return a FilePath (String) that is "pom.xml". If it is not, it will raise an error that says you aim to retrieve the head of an empty list.

    You thus likely can just work with:

        hasPomFile <- elem "pom.xml" <$> listDirectory path

    here hasPomFile is thus a Bool that is True if the directory contains a "pom.xml", and False otherwise.