我试图通过附加一个数字来查找目录名,直到找到一个尚未存在的名称:
head <$> filterM (fmap not . fexists_) [ getDatedDir t d n | n <- [0..] ]
这个问题是它永远不会回来。我认为问题是尽管IO是一个Functor,但filterM必须在头部效果之前完成所有IO;也就是说,它必须为每个n评估fexists - 当然这是无限的。
现在,我可以解决这个问题:
go t d 0
where go t d n = do
let dir = getDatedDir t d n
fexists_ dir >>= \case
False -> return dir
True -> go t d (n+1)
但是我觉得应该有一个更优雅的方法,使用filterM或类似的东西。
这感觉就像一个普通的模式,在Control.Monad中可能存在一个强大的功能,我只是没有看到它。
答案 0 :(得分:5)
来自firstM
的{{1}}:
Control.Monad.Loops
从列表中返回第一个值(如果有),满足给定的值 谓词。
答案 1 :(得分:2)
没有monad的等价物将是find :: Foldable t => (a -> Bool) -> t a -> Maybe a
。但我找不到一个允许它与monads一起工作的版本。
一个想法可能是自己实施findM
:
import Data.Bool(bool)
findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM f [] = return Nothing
findM f (x:xs) = f x >>= bool (findM f xs) (return (Just x))
然后您可以像:
一样使用它import System.Directory(doesFileExist)
main = do
Just fln <- findM (not . doesDirectoryExist) (map (getDatedDir t d) [0..])
putStrLn fln
打印第一个文件名,使文件不存在。当然,您也可以用不同的方式处理fln
。