这个功能可以吗?

时间:2011-10-07 21:43:34

标签: haskell

findM :: Monad m => (a -> m Bool) -> m [a] -> Maybe (m a)

我不能自己实现它。我可以使用一些指针

看起来像:

find f as = listToMaybe $ filter f as

所以我试过了:

findM f as = filterM f as >>= listToMaybe

但它不起作用。

4 个答案:

答案 0 :(得分:10)

没有。这不可能。但是,您可以使用签名编写一个函数:

findM :: Monad m => (a -> m Bool) -> m [a] -> m (Maybe a)

不可能的原因是,为了确定Maybe是否具有构造函数Just xNothing,您必须从monad中获取值。这意味着Maybe必须在最后的monad中,因为通常不可能从monad中提取值。 (毕竟,这是monad的重点。)

例如,鉴于您对findM的签名,您可能会写一些明显不正确的函数:

findM :: Monad m => (a -> m Bool) -> m [a] -> Maybe (m a)
findM = magic
anyM :: Monad m => (a -> m Bool) -> m [a] -> Bool
anyM f = isJust . findM f
extractBool :: Monad m => m Bool -> Bool
extractBool = anyM id . liftM (:[])

函数extractBool显然是不可能的,因此findM无法签名。

以下是findM的实现:

findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM _ [] = return Nothing
findM f (x:xs) = do y <- f x
                    if y then return (Just x) else findM f xs

我不确定一种更简单的方法来实现它 - 这看起来相当干净,并且可以在无限列表上运行。

将签名从使用m [a]更改为使用[a]会使其更有用,更易于使用。如果你实现这两个接口,你会很快找出原因。

答案 1 :(得分:7)

尝试

findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM p = foldr step (return Nothing)
 where
  step x r = do
    b <- p x
    if b then return (Just x) else r

此版本仅使用谓词,而filterM版本在每个元素上使用谓词。例如:

ghci> let findM' p xs = filterM p xs >>= return . listToMaybe
ghci> let check x = putStrLn ("checking " ++ show x) >> doesDirectoryExist x
ghci> findM check ["test1", ".", "test2"]
checking "test1"
checking "."
Just "."
ghci> findM' check ["test1", ".", "test2"]
checking "test1"
checking "."
checking "test2"
Just "."

答案 2 :(得分:1)

其他人已经证明这是不可能的,但是如果进一步限制你可以得到一个非常相似的功能。

findM :: (Traversable m, Monad m) => (a -> m Bool) -> m [a] -> Maybe (m a)
findM p xs = Data.Traverse.sequence $ dietrichsFindM p xs

并非每个Monad都有一个Traversable实例,但如果它有,这将有效。

答案 3 :(得分:0)

签名

findM :: Monad m => (a -> m Bool)  -> m [a] -> m (Maybe a)
我建议:

import Control.Monad
import Data.Maybe

findM :: Monad m => (a -> m Bool)  -> m [a] -> m (Maybe a)
findM f m = m >>= filterM f >>= return . listToMaybe

findM :: Monad m => (a -> m Bool)  -> m [a] -> m (Maybe a)
findM = ((return . listToMaybe =<<) .) . (=<<) . filterM

获得无点风格。