Say I have code where I want to do the following:
- Input: a list of strings
[String] - Operation (
checkSatandcheckResult)- Obtains a boolean from an input string.
- 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:
Note that both
filterMand>=>can be imported fromControl.Monad. I've redefined them here for the sake of clarity :-)The basic idea is to split the operation into two stages: (1) apply
checkSaton every item in the list; and (2) filter every item in the list. The first stage is given bytraverse checkSat, which has type[String] -> Either Err [String]. The second stage is given byfilterM (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.