[英]Haskell traverse and filter through a list while lifting results
假设我有要执行以下操作的代码:
[String]
checkSat
和checkResult
)
data Err = Err String deriving (Show)
-- Detail omitted for this example
checkSat :: String -> Either Err String
checkSat = Right . id
checkResult :: String -> Either Err Bool
checkResult "sat" = Right True
checkResult "unsat" = Right False
checkResult err = Left $ Err err
onlyUnsat :: [String] -> Either Err [String]
onlyUnsat xs = filter (traverse notSat xs) xs
where notSat x = fmap not $ checkSat x >>= checkResult
onlyUnsat = filter (traverse notSat xs) xs
上面的代码不太好用。 用正确的打字方式写onlyUnsat
的正确方法是什么?
尝试这个:
onlyUnsat :: [String] -> Either Err [String]
onlyUnsat = traverse checkSat >=> filterM (fmap not . checkResult)
where
filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
filterM p = fmap (map fst . filter snd) . traverse (\x -> withFst x <$> p x)
where withFst x = \y -> (x, y)
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
f >=> g = \x -> f x >>= g
请注意, filterM
和>=>
都可以从Control.Monad
导入。 为了清楚起见,我在这里重新定义了它们:-)
基本思想是将操作分为两个阶段:(1)对列表中的每个项目应用checkSat
; (2) 过滤列表中的每个项目。 第一阶段由traverse checkSat
给出,其类型为[String] -> Either Err [String]
。 第二阶段由filterM (fmap not . checkResult)
给出,它也有类型[String] -> Either Err String
。 然后我们使用>=>
将这两个计算链接在一起,形成一个相同的类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.