0

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) 
user65526
  • 685
  • 6
  • 19
  • 4
    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. – Willem Van Onsem Jul 11 '20 at 11:01
  • @Willem Van Onsem: sorry, what do you mean exactly? – user65526 Jul 11 '20 at 11:06
  • 3
    the class declaration of `Monoid` exists already, in the `Prelude`. Hence by defining another one, it is ambiguous to what `Monoid` you are referring, the one you defined in the shell? Or the one that was imported from the `Prelude`? – Willem Van Onsem Jul 11 '20 at 11:07

1 Answers1

4

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}
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Why do https://www.youtube.com/watch?v=bsp5pJlw6R0 (at 14:31) use the function ``coerce`` if it is not necessary to define a monoid on the natural numbers? I initially included ``coerce`` because that was what they did in their video. – user65526 Jul 11 '20 at 11:14
  • 1
    @user65526 `coerce` is an advanced tool that automatically adds/removes the `Sum` data constructor as needed to make the expression to type check. Essentially, it turns `(+) :: Num a => a -> a -> a` into `(+) :: Num a => Sum a -> Sum a -> Sum a`. If you are a beginner, it's best if you first learn how to do everything manually, using pattern matching (or `getSum`) and applying `Sum` as needed, without relying on too much automatic magic. – chi Jul 11 '20 at 11:26