过滤monad列表

时间:2017-01-05 16:27:11

标签: haskell

我正在尝试过滤类型列表:IO [Either a b]

理想情况下,我想用以下类型的sig组成过滤函数:

(Monad m, Monad m2) => m [m2 a] -> (a -> Bool) -> m [m2 a]

我尝试了很多filterfilterMfmap=<<的组合,徒劳地试图将我的谓词提升到适当的语境中,但我保持错过标记 - 我可以实现m [m a]-> (a -> m Bool)-> m [m a],但由于Either和IO不是同一个monad,这似乎对我没有好处。

这似乎是'do notation'的用例,但是我无法用一种方法来检查用<-运算符分配的事物的类型签名,所以我在移动时离开了目标

我不确定如何以一种明确表示它正在遍历包含不同monad(Either)实例的列表然后包含列表本身(IO)的monad的方式来编写函数。

我在这里缺少什么?

3 个答案:

答案 0 :(得分:5)

  

理想情况下,我想用以下内容组成过滤功能   type sig:

(Monad m, Monad m2) => m [m2 a] -> (a -> Bool) -> m [m2 a]

一般情况下无法完成,因为Monad界面无法从a中获取m a。您可以使用m [m2 Bool]获取fmap (fmap (fmap f)),但您需要从Bool中获取m2 Bool才能决定是否删除元素。

以这种方式看待它。如果有问题的monad是Proxy怎么办?

data Proxy a = Proxy  -- one constructor, no fields

instance Monad Proxy where
    return _ = Proxy
    Proxy >>= _ = Proxy

你无法查看Bool内的Proxy Bool,因为它里面什么都没有!鉴于a -> Bool[Proxy a],如何才能知道要删除列表中的哪些元素?

可以编写一个具有以下类型签名的函数:

myfilter :: (Functor f, Applicative g) => (a -> Bool) -> f [g a] -> f (g [a])

请注意,返回类型为f (g [a]),而不是f [g a]

此功能使用fmap进入外部仿函数,将列表中的应用g粉碎在一起,然后再次fmap来过滤结果。

myfilter p = fmap (fmap (filter p) . sequenceA)

答案 1 :(得分:1)

如果您可能无法从a中获取Monad m,则可以添加到已接受的答案中,您可以使用例如Monad m将您的功能添加到liftMIO[Either a b]。根据内部结构,你仍然可以编写可用的动作,然后最终以某种方式对它们进行排序,这样计算就会发生。以期望的方式。

举例说明使用IO[Either Bool Integer]作为import Control.Monad testCase :: IO [Either Bool Integer] testCase = return [(Right 1), (Left True), (Right 2), (Left False), (Right 3), (Right 4)] liftFilter :: Monad m => (a -> Bool) -> m [a] -> m [a] liftFilter pred = liftM (filter pred) testFilter :: (Either Bool Integer) -> Bool testFilter (Left True) = True testFilter (Right x) = even x testFilter _ = False showIt :: (Either Bool Integer) -> String showIt (Left True) = "Left True" showIt (Left False) = "Left False" showIt (Right x) = "Right x=" ++ (show x) test = do filtered <- liftFilter testFilter testCase return (fmap showIt filtered) runTest = do actions <- liftM (fmap putStrLn) test sequence_ actions 的问题:

composer.json

答案 2 :(得分:0)

我正在使用其monad实例而不是使用filter方法来处理列表。在现实世界中使用过滤器可能会更好。我也没有试图使我的功能尽可能小

我假设您想要一些任意方法来处理Eithers。 如果只想过滤“右边的a”或获取左边的颜色,则可能应该使用遍历。您还可以使用bimap来同时“左右”映射Either的左侧和右侧。

首先是仅适用于either的解决方案:

filtermonads :: IO [(Either a b)] -> (a -> Bool) -> IO [(Either a b)]
filtermonads ioListEitherA cond = do
  listEitherA <- ioListEitherA
  return $ do
        eitherA <- listEitherA
        case eitherA of
          Left a -> if cond a then pure (Left a) else []
          Right b -> return (Right b)

应与以下相同:

filtermonads' :: IO [(Either a b)] -> (a -> Bool) -> IO [(Either a b)]
filtermonads' ioListEitherA cond = let
  f x = case x of
    Left a -> if cond a then pure (Left a) else []
    Right a -> pure (Right a)
  in
    fmap (join . fmap f) ioListEitherA

我们必须手动打开Either,因为我们需要访问其中封装的信息才能知道如何处理列表。

您可以通过单独提供一个函数(m2 a -> Bool)来概括这一点,该函数可以告诉您如何从monad中提取信息。

eitherCond :: Either a b -> Bool
eitherCond eitherA = case eitherA of
          Left a -> False
          Right b -> True

filtermonads'' :: (Monad m) => m [a] -> (a -> Bool) -> m [a]
filtermonads'' m1listm2a cond = do
  listm2a <- m1listm2a
  return $ do
    m2a <- listm2a
    if cond m2a then pure m2a else []

您还可以将列表概括为MonadPlusMonadFail,因为您可以想到过滤意味着您需要一个零元素的monad。 monad库本身的失败有时为零,但您不应该使用它,因为它不是普通monad所具有的功能,因此实现有时是非常任意的。

纯过滤器可能更好,因为只有很少的结构实现MonadPlus类。我应该为此使用mfilterMonadPlus也没有太多实现它的结构。例如,地图甚至不是单子。

filtermonads''' :: (Monad m1, MonadPlus mp) => m1 (mp a) -> (a -> Bool) -> m1 (mp a)
filtermonads''' m1listm2a cond = do
  listm2a <- m1listm2a
  return $ do
    m2a <- listm2a
    if cond m2a then pure m2a else mzero
testData :: IO [Either Char Char]
testData = pure [Right 'a', Left 'a', Left 'c']

test1 = filtermonads testData (\x -> x=='a')
test2 = filtermonads' testData (\x -> x=='a')
test3 = filtermonads'' testData eitherCond
test4 = filtermonads''' testData eitherCond

justData ::IO (Maybe Char)
justData = pure (Just 'b')
readData :: Maybe (IO Char)
readData = Just (getChar) -- Notice that io mzero is error!
test5 = filtermonads''' justData (\x -> x=='a')
test6 = filtermonads''' readData (\x -> x=='a') 
main = case (test6) of
  Just a -> a >>= putStrLn . pure
  Nothing -> pure ()