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.
1 Like
I don’t think it’s a monad, is it? Consider
CollectPure w1 () >> (CollectPure w2 () >> CollectM w3 (pure ())
== CollectPure w1 () >> (CollectM w2 (pure ())
== CollectM w1 (pure ())
whereas
(CollectPure w1 () >> CollectPure w2 ()) >> CollectM w3 (pure ())
== CollectPure (w1 <> w2) >> CollectM w3 (pure ())
== CollectM (w1 <> w2) (pure ())
2 Likes
ah right, what if you always merge the monoid on CollectPure?
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 w2 mb -> CollectM (w1 <> w2) mb
CollectM w ma >>= k = CollectM w $ do
a <- ma
case k a of
CollectPure _ b -> pure b
CollectM _ mb -> mb
1 Like
That looks more likely to be a monad, but I’m not sure it any longer does what you initally wanted.
It should for my purposes, but perhaps my initial description is no longer accurate.
Ultimately, I want
getCfg :: CollectM Cfg m a -> Cfg
getCfg = \case
CollectPure cfg _ -> cfg
CollectM cfg _ -> cfg
to return the merged configs from the longest prefix of CollectPure’s. I think this does that?
Yes I think so (plus the config from the first CollectM
).