如何使用带有无限列表的filterM

时间:2017-11-13 20:24:07

标签: haskell monads

我试图通过附加一个数字来查找目录名,直到找到一个尚未存在的名称:

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中可能存在一个强大的功能,我只是没有看到它。

2 个答案:

答案 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