summaryrefslogtreecommitdiff
path: root/libraries/base/Data/Monoid.hs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/base/Data/Monoid.hs')
-rw-r--r--libraries/base/Data/Monoid.hs300
1 files changed, 156 insertions, 144 deletions
diff --git a/libraries/base/Data/Monoid.hs b/libraries/base/Data/Monoid.hs
index 6ccdb34045..cf55b2150c 100644
--- a/libraries/base/Data/Monoid.hs
+++ b/libraries/base/Data/Monoid.hs
@@ -1,9 +1,9 @@
-{-# LANGUAGE Trustworthy #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE PolyKinds #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy #-}
-----------------------------------------------------------------------------
-- |
@@ -16,8 +16,43 @@
-- Stability : experimental
-- Portability : portable
--
--- A class for monoids (types with an associative binary operation that
--- has an identity) with various general-purpose instances.
+-- A type @a@ is a 'Monoid' if it provides an associative function ('<>')
+-- that lets you combine any two values of type @a@ into one, and a neutral
+-- element (`mempty`) such that
+--
+-- > a <> mempty == mempty <> a == a
+--
+-- A 'Monoid' is a 'Semigroup' with the added requirement of a neutral element.
+-- Thus any 'Monoid' is a 'Semigroup', but not the other way around.
+--
+-- ==== __Examples__
+--
+-- The 'Sum' monoid is defined by the numerical addition operator and `0` as neutral element:
+--
+-- >>> mempty :: Sum Int
+-- Sum 0
+-- >>> Sum 1 <> Sum 2 <> Sum 3 <> Sum 4 :: Sum Int
+-- Sum {getSum = 10}
+--
+-- We can combine multiple values in a list into a single value using the `mconcat` function.
+-- Note that we have to specify the type here since 'Int' is a monoid under several different
+-- operations:
+--
+-- >>> mconcat [1,2,3,4] :: Sum Int
+-- Sum {getSum = 10}
+-- >>> mconcat [] :: Sum Int
+-- Sum {getSum = 0}
+--
+-- Another valid monoid instance of 'Int' is 'Product' It is defined by multiplication
+-- and `1` as neutral element:
+--
+-- >>> Product 1 <> Product 2 <> Product 3 <> Product 4 :: Product Int
+-- Product {getProduct = 24}
+-- >>> mconcat [1,2,3,4] :: Product Int
+-- Product {getProduct = 24}
+-- >>> mconcat [] :: Product Int
+-- Product {getProduct = 1}
+--
--
-----------------------------------------------------------------------------
@@ -38,131 +73,25 @@ module Data.Monoid (
First(..),
Last(..),
-- * 'Alternative' wrapper
- Alt (..)
+ Alt(..),
+ -- * 'Applicative' wrapper
+ Ap(..)
) where
-- Push down the module in the dependency hierarchy.
import GHC.Base hiding (Any)
import GHC.Enum
+import GHC.Generics
import GHC.Num
import GHC.Read
import GHC.Show
-import GHC.Generics
-
-{-
--- just for testing
-import Data.Maybe
-import Test.QuickCheck
--- -}
-
-infixr 6 <>
-
--- | An infix synonym for 'mappend'.
---
--- @since 4.5.0.0
-(<>) :: Monoid m => m -> m -> m
-(<>) = mappend
-{-# INLINE (<>) #-}
-
--- Monoid instances.
-
--- | The dual of a 'Monoid', obtained by swapping the arguments of 'mappend'.
-newtype Dual a = Dual { getDual :: a }
- deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1)
-
--- | @since 2.01
-instance Monoid a => Monoid (Dual a) where
- mempty = Dual mempty
- Dual x `mappend` Dual y = Dual (y `mappend` x)
-
--- | @since 4.8.0.0
-instance Functor Dual where
- fmap = coerce
-
--- | @since 4.8.0.0
-instance Applicative Dual where
- pure = Dual
- (<*>) = coerce
-
--- | @since 4.8.0.0
-instance Monad Dual where
- m >>= k = k (getDual m)
-
--- | The monoid of endomorphisms under composition.
-newtype Endo a = Endo { appEndo :: a -> a }
- deriving (Generic)
-
--- | @since 2.01
-instance Monoid (Endo a) where
- mempty = Endo id
- Endo f `mappend` Endo g = Endo (f . g)
-
--- | Boolean monoid under conjunction ('&&').
-newtype All = All { getAll :: Bool }
- deriving (Eq, Ord, Read, Show, Bounded, Generic)
-
--- | @since 2.01
-instance Monoid All where
- mempty = All True
- All x `mappend` All y = All (x && y)
-
--- | Boolean monoid under disjunction ('||').
-newtype Any = Any { getAny :: Bool }
- deriving (Eq, Ord, Read, Show, Bounded, Generic)
-
--- | @since 2.01
-instance Monoid Any where
- mempty = Any False
- Any x `mappend` Any y = Any (x || y)
--- | Monoid under addition.
-newtype Sum a = Sum { getSum :: a }
- deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Num)
+import Control.Monad.Fail (MonadFail)
--- | @since 2.01
-instance Num a => Monoid (Sum a) where
- mempty = Sum 0
- mappend = coerce ((+) :: a -> a -> a)
--- Sum x `mappend` Sum y = Sum (x + y)
-
--- | @since 4.8.0.0
-instance Functor Sum where
- fmap = coerce
-
--- | @since 4.8.0.0
-instance Applicative Sum where
- pure = Sum
- (<*>) = coerce
-
--- | @since 4.8.0.0
-instance Monad Sum where
- m >>= k = k (getSum m)
-
--- | Monoid under multiplication.
-newtype Product a = Product { getProduct :: a }
- deriving (Eq, Ord, Read, Show, Bounded, Generic, Generic1, Num)
-
--- | @since 2.01
-instance Num a => Monoid (Product a) where
- mempty = Product 1
- mappend = coerce ((*) :: a -> a -> a)
--- Product x `mappend` Product y = Product (x * y)
-
--- | @since 4.8.0.0
-instance Functor Product where
- fmap = coerce
-
--- | @since 4.8.0.0
-instance Applicative Product where
- pure = Product
- (<*>) = coerce
-
--- | @since 4.8.0.0
-instance Monad Product where
- m >>= k = k (getProduct m)
+import Data.Semigroup.Internal
-- $MaybeExamples
--- To implement @find@ or @findLast@ on any 'Foldable':
+-- To implement @find@ or @findLast@ on any 'Data.Foldable.Foldable':
--
-- @
-- findLast :: Foldable t => (a -> Bool) -> t a -> Maybe a
@@ -171,20 +100,20 @@ instance Monad Product where
-- else Last Nothing)
-- @
--
--- Much of Data.Map's interface can be implemented with
--- Data.Map.alter. Some of the rest can be implemented with a new
--- @alterA@ function and either 'First' or 'Last':
+-- Much of 'Data.Map.Lazy.Map's interface can be implemented with
+-- 'Data.Map.Lazy.alter'. Some of the rest can be implemented with a new
+-- 'Data.Map.Lazy.alterF' function and either 'First' or 'Last':
--
--- > alterA :: (Applicative f, Ord k) =>
+-- > alterF :: (Functor f, Ord k) =>
-- > (Maybe a -> f (Maybe a)) -> k -> Map k a -> f (Map k a)
-- >
--- > instance Monoid a => Applicative ((,) a) -- from Control.Applicative
+-- > instance Monoid a => Functor ((,) a) -- from Data.Functor
--
-- @
-- insertLookupWithKey :: Ord k => (k -> v -> v -> v) -> k -> v
-- -> Map k v -> (Maybe v, Map k v)
-- insertLookupWithKey combine key value =
--- Arrow.first getFirst . alterA doChange key
+-- Arrow.first getFirst . 'Data.Map.Lazy.alterF' doChange key
-- where
-- doChange Nothing = (First Nothing, Just value)
-- doChange (Just oldValue) =
@@ -197,41 +126,121 @@ instance Monad Product where
--
-- @'First' a@ is isomorphic to @'Alt' 'Maybe' a@, but precedes it
-- historically.
+--
+-- >>> getFirst (First (Just "hello") <> First Nothing <> First (Just "world"))
+-- Just "hello"
+--
+-- Use of this type is discouraged. Note the following equivalence:
+--
+-- > Data.Monoid.First x === Maybe (Data.Semigroup.First x)
+--
+-- In addition to being equivalent in the structural sense, the two
+-- also have 'Monoid' instances that behave the same. This type will
+-- be marked deprecated in GHC 8.8, and removed in GHC 8.10.
+-- Users are advised to use the variant from "Data.Semigroup" and wrap
+-- it in 'Maybe'.
newtype First a = First { getFirst :: Maybe a }
- deriving (Eq, Ord, Read, Show, Generic, Generic1,
- Functor, Applicative, Monad)
+ deriving ( Eq -- ^ @since 2.01
+ , Ord -- ^ @since 2.01
+ , Read -- ^ @since 2.01
+ , Show -- ^ @since 2.01
+ , Generic -- ^ @since 4.7.0.0
+ , Generic1 -- ^ @since 4.7.0.0
+ , Functor -- ^ @since 4.8.0.0
+ , Applicative -- ^ @since 4.8.0.0
+ , Monad -- ^ @since 4.8.0.0
+ )
+
+-- | @since 4.9.0.0
+instance Semigroup (First a) where
+ First Nothing <> b = b
+ a <> _ = a
+ stimes = stimesIdempotentMonoid
-- | @since 2.01
instance Monoid (First a) where
mempty = First Nothing
- First Nothing `mappend` r = r
- l `mappend` _ = l
-- | Maybe monoid returning the rightmost non-Nothing value.
--
-- @'Last' a@ is isomorphic to @'Dual' ('First' a)@, and thus to
-- @'Dual' ('Alt' 'Maybe' a)@
+--
+-- >>> getLast (Last (Just "hello") <> Last Nothing <> Last (Just "world"))
+-- Just "world"
+--
+-- Use of this type is discouraged. Note the following equivalence:
+--
+-- > Data.Monoid.Last x === Maybe (Data.Semigroup.Last x)
+--
+-- In addition to being equivalent in the structural sense, the two
+-- also have 'Monoid' instances that behave the same. This type will
+-- be marked deprecated in GHC 8.8, and removed in GHC 8.10.
+-- Users are advised to use the variant from "Data.Semigroup" and wrap
+-- it in 'Maybe'.
newtype Last a = Last { getLast :: Maybe a }
- deriving (Eq, Ord, Read, Show, Generic, Generic1,
- Functor, Applicative, Monad)
+ deriving ( Eq -- ^ @since 2.01
+ , Ord -- ^ @since 2.01
+ , Read -- ^ @since 2.01
+ , Show -- ^ @since 2.01
+ , Generic -- ^ @since 4.7.0.0
+ , Generic1 -- ^ @since 4.7.0.0
+ , Functor -- ^ @since 4.8.0.0
+ , Applicative -- ^ @since 4.8.0.0
+ , Monad -- ^ @since 4.8.0.0
+ )
+
+-- | @since 4.9.0.0
+instance Semigroup (Last a) where
+ a <> Last Nothing = a
+ _ <> b = b
+ stimes = stimesIdempotentMonoid
-- | @since 2.01
instance Monoid (Last a) where
mempty = Last Nothing
- l `mappend` Last Nothing = l
- _ `mappend` r = r
--- | Monoid under '<|>'.
+-- | This data type witnesses the lifting of a 'Monoid' into an
+-- 'Applicative' pointwise.
--
--- @since 4.8.0.0
-newtype Alt f a = Alt {getAlt :: f a}
- deriving (Generic, Generic1, Read, Show, Eq, Ord, Num, Enum,
- Monad, MonadPlus, Applicative, Alternative, Functor)
-
--- | @since 4.8.0.0
-instance Alternative f => Monoid (Alt f a) where
- mempty = Alt empty
- mappend = coerce ((<|>) :: f a -> f a -> f a)
+-- @since 4.12.0.0
+newtype Ap f a = Ap { getAp :: f a }
+ deriving ( Alternative -- ^ @since 4.12.0.0
+ , Applicative -- ^ @since 4.12.0.0
+ , Enum -- ^ @since 4.12.0.0
+ , Eq -- ^ @since 4.12.0.0
+ , Functor -- ^ @since 4.12.0.0
+ , Generic -- ^ @since 4.12.0.0
+ , Generic1 -- ^ @since 4.12.0.0
+ , Monad -- ^ @since 4.12.0.0
+ , MonadFail -- ^ @since 4.12.0.0
+ , MonadPlus -- ^ @since 4.12.0.0
+ , Ord -- ^ @since 4.12.0.0
+ , Read -- ^ @since 4.12.0.0
+ , Show -- ^ @since 4.12.0.0
+ )
+
+-- | @since 4.12.0.0
+instance (Applicative f, Semigroup a) => Semigroup (Ap f a) where
+ (Ap x) <> (Ap y) = Ap $ liftA2 (<>) x y
+
+-- | @since 4.12.0.0
+instance (Applicative f, Monoid a) => Monoid (Ap f a) where
+ mempty = Ap $ pure mempty
+
+-- | @since 4.12.0.0
+instance (Applicative f, Bounded a) => Bounded (Ap f a) where
+ minBound = pure minBound
+ maxBound = pure maxBound
+
+-- | @since 4.12.0.0
+instance (Applicative f, Num a) => Num (Ap f a) where
+ (+) = liftA2 (+)
+ (*) = liftA2 (*)
+ negate = fmap negate
+ fromInteger = pure . fromInteger
+ abs = fmap abs
+ signum = fmap signum
{-
{--------------------------------------------------------------------
@@ -253,3 +262,6 @@ prop_mconcatLast x =
where listLastToMaybe [] = Nothing
listLastToMaybe lst = Just (last lst)
-- -}
+
+-- $setup
+-- >>> import Prelude