Came across a pattern today, curious if anyone knows if it’s used anywhere today:
data CollectM w m a
= CollectPure w a
| CollectM w (m a)
instance Functor m => Functor (CollectM w m) where
fmap f = \case
CollectPure w a -> CollectPure w (f a)
CollectM w m -> CollectM w (f <$> m)
instance (Monoid w, Monad m) => Applicative (CollectM w m) where
pure = CollectPure mempty
(<*>) = ap
instance (Monoid w, Monad m) => Monad (CollectM w m) where
CollectPure w1 a >>= k =
case k a of
CollectPure w2 b -> CollectPure (w1 <> w2) b
CollectM _ mb -> CollectM w1 mb
CollectM w ma >>= k = CollectM w $ do
a <- ma
case k a of
CollectPure _ b -> pure b
CollectM _ mb -> mb
The general idea is all CollectPure’s at the beginning merge, then the collected monoid is propagated read-only to the rest of the compution.