Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do these monoid instances exist somewhere already?

Tags:

haskell

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

like image 525
Cubic Avatar asked Mar 29 '14 21:03

Cubic


3 Answers

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.

like image 116
duplode Avatar answered Oct 20 '22 01:10

duplode


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.

like image 28
Boyd Stephen Smith Jr. Avatar answered Oct 19 '22 23:10

Boyd Stephen Smith Jr.


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

like image 1
sclv Avatar answered Oct 20 '22 00:10

sclv