Can I use SYB's gfoldl to do the map over the result of listify in one go?
Consider for example the following code:
extractNums :: Expr -> [Int]
extractNums e = map numVal $ listify isNum e
where isNum :: Expr -> Bool
isNum (Num _) = True
isNum _ = False
numVal :: Expr -> Int
numVal (Num i) = i
numVal _ = error "Somehow filter did not work?"
I do not like that in the numVal function I have to consider the different data constructors of the Expr type while I am only interested in the Num constructor. I rather would replace isNum and numVals with something like the vals function below:
vals :: [Int] -> Expr -> [Int]
vals xs (Num x) = x : xs
vals xs _ = xs
Can this be done with gfoldl? How?
Function listify
is defined as
-- | Get a list of all entities that meet a predicate
listify :: Typeable r => (r -> Bool) -> GenericQ [r]
listify p = everything (++) ([] `mkQ` (\x -> if p x then [x] else []))
which is similar to filter
. We could create an alternative that resembles mapMaybe
which combines map
and filter
you need into one:
import Data.Generics
import Data.Generics.Schemes
import Data.Maybe (maybeToList)
import Data.Typeable
listify' :: (Typeable t) => (t -> Maybe r) -> GenericQ [r]
listify' f = everything (++) ([] `mkQ` (maybeToList . f))
Then your example could be expressed as
numVal :: Expr -> Maybe Int
numVal (Num i) = Just i
numVal _ = Nothing
test :: Expr -> [Int]
test = listify' numVal