Emre Sevinç
Emre Sevinç

Reputation: 8511

How to write Semigroup instance for this data type?

I'm trying to do one of the Semigroup exercises in Haskell Book (Chapter 15, "Monoid, Semigroup") but I'm stuck. The following is given:

newtype Combine a b =
  Combine { unCombine :: (a -> b) }

and I'm supposed to write the Semigroup instance for Combine.

And then book says that it must behave like the following:

 Prelude> let f = Combine $ \n -> Sum (n + 1)
 Prelude> let g = Combine $ \n -> Sum (n - 1)
 Prelude> unCombine (f <> g) $ 0
 Sum {getSum = 0}
 Prelude> unCombine (f <> g) $ 1
 Sum {getSum = 2}
 Prelude> unCombine (f <> f) $ 1
 Sum {getSum = 4}
 Prelude> unCombine (g <> f) $ 1
 Sum {getSum = 2}

So I first started with a wrong solution that type checks:

instance Semigroup (Combine a b) where
  Combine f <> Combine g = Combine f

That does not what is expected of course, but hopefully a step in the right direction. And my thinking is something like the following, in pseudocode:

 instance Semigroup (Combine a b) where
   (Combine f) <> (Combine g) = Combine (SOMETHING)

That SOMETHING being: f and g appended, whatever that concrete append operation is (it depends on f and g); so I think this requires <> from Data.Monoid, but I already have import Data.Semigroup in my code, and therefore <> from Data.Monoid coincides with the one from Data.Semigroup. So what am I supposed to do?

I tried to find out how I can state something like "Combine (f Monoid's <> g)", but couldn't find out.

The book also states unless I'm using GHC 8.x, I'll have to import Semigroup and that I might have "shadow" the <> from Monoid; but I'm struggling to find out how to have this effect.

Any ideas?

Upvotes: 4

Views: 1171

Answers (2)

V. Semeria
V. Semeria

Reputation: 3256

What they want is probably the addition of functions. For that, type b needs to be a Semigroup :

import Data.Semigroup

newtype Combine a b =
  Combine { unCombine :: (a -> b) }

instance Semigroup b => Semigroup (Combine a b) where
  (Combine f) <> (Combine g) = Combine (\x -> f x <> g x)

Upvotes: 14

Daniel Wagner
Daniel Wagner

Reputation: 152707

You can shadow Monoid's (<>) this way:

import Data.Monoid hiding ((<>))

Then when you import Data.Semigroup, you will have just one (<>) in scope: the one from Data.Semigroup.

Upvotes: 6

Related Questions