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.
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.