Search code examples
haskellmonoids

Further problems with the monoid type class


I am trying to toy around with monoids using the Semigroup typeclass, and I am trying to define a monoid on the natural numbers. I put the following class and instance declarations into GHCI

Prelude:{
Prelude| class Semigroup a where
Prelude|    (<>) :: a -> a -> a)
Prelude| newtype Sum a = Sum { getSum :: a }
Prelude|     deriving (Eq, Ord, Show)
Prelude| instance Num a => Monoid (Sum a) where
Prelude|    (<>) = coerce ((+) :: a -> a -> a)
Prelude| instance Num a => Monoid (Sum a) where
Prelude|     mempty = Sum 0
Prelude| :} 

I receive the message:

<interactive>:7:4: error:
Ambiguous occurrence ‘<>’
It could refer to either ‘Prelude.<>’,
                         imported qualified from ‘Prelude’
                         (and originally defined in ‘GHC.Base’)
                      or ‘<>’, defined at <interactive>:3:4

I then entered import qualified Prelude as P, so as to avoid the clash, but this does not work, and I get the error message:

code<interactive>:26:19: error:
Not in scope: type constructor or class ‘Monoid’
Perhaps you meant one of these:
  ‘P.Monoid’ (imported from Prelude),
  ‘P.Monad’ (imported from Prelude) 

Solution

  • You should not define the typeclass yourself. It is already defined, by defining another one, it means that all your (<>) functions, etc. now can point to the Semigroup you defined, or the one that was defined in the Prelude.

    You thus can define your type and make it an instance of Semigroup and Monoid with:

    import Data.Semigroup
    
    newtype Sum a = Sum { getSum :: a } deriving (Eq, Ord, Show)
    
    instance Num a => Semigroup (Sum a) where
        Sum a <> Sum b = Sum (a + b)
    
    instance Num a => Monoid (Sum a) where
        mempty = Sum 0
        mappend = (<>)

    If we thus run this in the shell, we get:

    Prelude> import Data.Semigroup
    Prelude Data.Semigroup> :{
    Prelude Data.Semigroup| newtype Sum a = Sum { getSum :: a } deriving (Eq, Ord, Show)
    Prelude Data.Semigroup| 
    Prelude Data.Semigroup| instance Num a => Semigroup (Sum a) where
    Prelude Data.Semigroup|     Sum a <> Sum b = Sum (a + b)
    Prelude Data.Semigroup| 
    Prelude Data.Semigroup| instance Num a => Monoid (Sum a) where
    Prelude Data.Semigroup|     mempty = Sum 0
    Prelude Data.Semigroup|     mappend = (<>)
    Prelude Data.Semigroup| :}
    Prelude Data.Semigroup> Sum 0 <> Sum 1 <> Sum 4 <> Sum 5
    Sum {getSum = 10}