The most naive solution would be to simply do both evalutations independently:
catMaybesCount :: [Maybe a] -> ([a], Int)
catMaybesCount xs = (catMaybes xs, length $ filter isNothing xs)
I don't know if GHC is able to optimize this properly, but the length . filter p
solution for counting Nothings
has some peculiarities anyway (see this SO post for an overview).
Theoretically, this solution could require two passes over the list, instead of the one
This is a recursive solution solving this issue I came up with:
import Data.Maybe
-- | Equivalent to @catMaybes@, but additonally counts @Nothing@ values
catMaybesCount :: [Maybe a] -> ([a], Int)
catMaybesCount xs = catMaybesCountWorker xs [] 0
-- | Worker function for @catMaybesCount@
catMaybesCountWorker :: [Maybe a] -> [a] -> Int -> ([a], Int)
catMaybesCountWorker [] justs cnt = (justs, cnt)
catMaybesCountWorker (Nothing:xs) justs cnt =
catMaybesCountWorker xs justs (cnt + 1)
catMaybesCountWorker ((Just v):xs) justs cnt =
catMaybesCountWorker xs (justs ++ [v]) cnt
As applying it to a list should evaluate the list only once, this should be more efficient.
However I am worried about the justs ++ [v]
anti-idiom, as (:)
would be more efficient (see this discussion). However, this would invert the resulting list. Maybe someone with more knowledge on this topic could have a look at it?
Note that this function won't terminate for infinite lists because the Nothing
count will never finish to evaluate.