"Somewhere" being "in the standard library or in some package that's small and general enough to make it a relatively harmless dependency".
import qualified Data.Map as M
import Data.Monoid
import Control.Applicative
newtype MMap k v = MMap {unMMap :: M.Map k v}
newtype MApplictive f a = MApplicative {unMApplicative :: f a}
-- M.unionWith f M.empty m = M.unionWith f m M.empty = m
-- f a (f b c) = f (f a b) c =>
-- M.unionWith f m1 (M.unionWith f m2 m3) =
-- M.unionWith f (M.unionWith f m1 m2) m3
instance (Ord k, Monoid v) => Monoid (MMap k v) where
mempty = MMap $ M.empty
mappend m1 m2 = MMap $ M.unionWith mappend (unMMap m1) (unMMap m2)
instance (Applicative f, Monoid a) => Monoid (MApplicative f a) where
mempty = MApplicative $ pure mempty
mappend f1 f2 = MApplicative $ liftA2 mappend (unMApplicative f1) (unMApplicative f2)
(These instances should satisfy the monoid laws - didn't bother to prove it for the applicative one though)
I'm asking because I have some use for both of those and I don't like to redefine things that are already there.
These instances exist in reducers, an Edward Kmett package. Your MApplicative
is known there as Ap
, while MMap
is encoded through the Union
newtype. Since base-4.12, Ap
has also been available from Data.Monoid
.
Something like this?
class Functor f => Monoidal f where
fempty :: Monoid m => f m
fempty = fconcat []
fappend :: Monoid m => f m -> f m -> f m
fappend l r = fconcat [l, r]
fconcat :: (Foldable c, Monoid m) => c (f m) -> f m
fconcat = unMWrap $ foldMap MWrap
{-# MINIMAL fempty, fappend | fconcat #-}
-- Could just be Pointed instead of Applicative, but that's not in base
applicativeFEmpty :: (Applicative f, Monoid m) => f m
applicativeFEmpty = pure mempty
applicativeFAppend :: (Applicative f, Monoid m) => f m -> f m -> f m
applicativeFAppend = liftA2 mappend
applicativeFConcat :: (Applicative f, Monoid m, Foldable c) => c (f m) -> f m
applicativeFConcat = fmap mconcat . sequenceA . foldMap (:[])
newtype MonoidWrap f a = MWrap { unMWrap :: f a }
instance Monoidal f, Monoid m => Monoid (MonoidWrap f m) where
mempty = MWrap $ fempty . unMWrap
mappend l r = MWrap $ fappend (unMWap l) (unMWrap r)
mconcat = MWrap $ fconcat . map unMWrap
Plus, Monoidal instances for all the suitable data types in base? It wouldn't cover Data.Map.Map which is actually my most common use of this pattern, but that could be added simply enough.
Not quite sure about the recursion between mconcat and fconcat. Could be a problem.
I think the answer to this question is "No," which is why it has remained without a positive answer for so long.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With