[英]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.