在 IO monad 中获取列表的第一个元素

Getting a first element of the list within IO monad

我正在尝试从 IO monad 链中获取第一个元素(据我了解,我仍在学习 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]

当我尝试应用 head 函数时出现错误:

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

我也试过:

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

没有结果。我应该如何将 head 函数应用于 IO [a] 类型以使其结果为 IO a?是否有 head 等效于将其应用于 monad?我知道我可以只使用 System.Directory.findFile 或类似的东西,但我想了解为什么我的方法不起作用。

您不能使用 head,因为这里的参数是 IO [a],而不是 a。但是,您可以在此基础上 fmap :: Function f => (a -> b) -> f a -> f b,从而获得 IO a,或使用运算符别名 <$>.

也就是说,这里使用filterM 不需要,您可以只过滤列表,然后取头部:

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

然而以上内容并不安全。如果目录确实包含 "pom.xml",那么它将简单地 return 一个 FilePath (String) 即 "pom.xml"。如果不是,它会引发一个错误,表明您的目标是检索空列表的头部。

因此您可能只需要使用:

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

这里 hasPomFileBool,如果目录包含 "pom.xml",则 True,否则 False