Cubic
Cubic

Reputation: 15673

Do these monoid instances exist somewhere already?

"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.

Upvotes: 16

Views: 533

Answers (3)

duplode
duplode

Reputation: 34378

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.

Upvotes: 2

sclv
sclv

Reputation: 38893

I think the answer to this question is "No," which is why it has remained without a positive answer for so long.

Upvotes: 1

Boyd Stephen Smith Jr.
Boyd Stephen Smith Jr.

Reputation: 3202

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.

Upvotes: 1

Related Questions