简体   繁体   中英

Haskell traverse and filter through a list while lifting results

Say I have code where I want to do the following:

  1. Input: a list of strings [String]
  2. Operation ( checkSat and checkResult )
    • Obtains a boolean from an input string.
  3. Output:
    • If everything in the input is parseable into a boolean, only return those that resulted in "unsat".
    • If at least one of them has an error, return an error.
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

The code above doesn't quite work. What's the right way to write onlyUnsat with the correct typing?

Try this:

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

Note that both filterM and >=> can be imported from Control.Monad . I've redefined them here for the sake of clarity :-)

The basic idea is to split the operation into two stages: (1) apply checkSat on every item in the list; and (2) filter every item in the list. The first stage is given by traverse checkSat , which has type [String] -> Either Err [String] . The second stage is given by filterM (fmap not . checkResult) , which also has type [String] -> Either Err String . We then use >=> to chain these two computations together into one of the same type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM