Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a Semigroup Instance?

Problem

Given a data type, implement the Semigroup instance. Heres the data type I was given to implement: data Or a b = Fst a | Snd b deriving (Eq, Show, Num). And it should function like so:

Prelude> Fst 1 <> Snd 2
Snd 2
Prelude> Fst 1 <> Fst 2
Fst 2
Prelude> Snd 1 <> Fst 2
Snd 1
Prelude> Snd 1 <> Snd 2
Snd 1

When I test values like > Fst "help" <> Fst "me" it works correctly, but when I test other values I get errors. When I try to fix these errors by deriving the classes from the errors, I get more errors. What am I doing wrong here?

My Code

data Or a b =
    Fst a
  | Snd b
  deriving (Eq, Show)

instance (Semigroup a, Semigroup b, Num a, Num b) => Semigroup (Or a b) where
 (Snd a) <> _ = Snd a
  _ <> (Snd a) = Snd a
  (Fst a) <> (Fst b) = Fst b  

Error

When I try to test with integers > Fst 1 <> Fst 2 I get:

No instance for (Num a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
  instance RealFloat a => Num (Data.Complex.Complex a)
    -- Defined in ‘Data.Complex’
  instance Data.Fixed.HasResolution a => Num (Data.Fixed.Fixed a)
    -- Defined in ‘Data.Fixed’
  instance forall (k :: BOX) (f :: k -> *) (a :: k).
           Num (f a) =>
           Num (Data.Monoid.Alt f a)
    -- Defined in ‘Data.Monoid’
  ...plus 21 others
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it

When I try to derive the Num class data Or a b = Fst a | Snd b deriving (Eq, Show, Num) I get:

    Can't make a derived instance of ‘Num (Or a b)’:
      ‘Num’ is not a derivable class

    In the data declaration for ‘Or’
Failed, modules loaded: none.
like image 446
Jonathan Portorreal Avatar asked Dec 24 '22 02:12

Jonathan Portorreal


1 Answers

The real problem is the constraint you've put on your instance, which you don't need. Just write

instance Semigroup (Or a b) where
  (Snd a) <> _ = Snd a
  _ <> (Snd a) = Snd a
  (Fst a) <> (Fst b) = Fst b

As chepner shows, you can actually cut down the number of lines and the number of patterns, and return one of the arguments as the result instead of constructing a new copy of it. That's potentially good for efficiency.

Less importantly, the parentheses in your patterns are all redundant, because application has higher precedence than any operator.

like image 151
dfeuer Avatar answered Jan 01 '23 20:01

dfeuer