3
votes

Lets see declaration of a new data type used to deal with reverse list in Haskell:

import Data.Monoid 
data RevList a = Nil | RCons (RevList a) a deriving (Eq, Show)

instance Monoid a => Monoid (RevList a) where
    mempty = Nil

instance Semigroup a => Monoid (RevList a) where
    Nil <> RCons (RevList a) a = RCons (RevList a) a
    RCons (RevList a) a <> RNil = RCons (RevList a) a
    Nil <> Nil = Nil

The problem I'm troubled with is a compilation failure which description looks as follows:

 `<>' is not a (visible) method of class `Monoid'

First I've tried to create a Monoid instance without any declaration of a Semigroup instance, but it caused another failure managed after reading this question. So, what's wrong with '<>' in the current stuff? Be sure, I'm aware of missing Monoid function like mappend or mconcat to be added to Monoid instance code as compulsory ones.

2

2 Answers

5
votes

The instance defining <> should be for Semigroup (RevList a), not for Monoid (RevList a), because <> is a Semigroup method.

I'm aware of missing Monoid function like mappend or mconcat to be added to Monoid instance code as compulsory ones

They aren't actually compulsory, you can tell this from

Minimal complete definition

mempty

in http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Monoid.html.

You also aren't actually using the constraints in your instances (the part before =>); e.g. you aren't calling mempty :: a in your implementation of mempty :: RevList a. So they can be removed and you end up with

instance Monoid (RevList a) where
    mempty = RNil

instance Semigroup (RevList a) where
    RNil <> RCons (RevList a) a = RCons (RevList a) a
    RCons (RevList a) a <> RNil = RCons (RevList a) a
    RNil <> RNil = RNil
1
votes
instance Semigroup [a] where
   (<>)  =  (++)

saves us having to define

append (x:xs) ys  =  x : append xs ys     -- x is the first
append    []  ys  =                ys

Treating the same regular list data type (x:xs) as the representation of the reversed list with x at its end, we'd have to define:

apprev xs (y:ys)  =  y : apprev xs ys     -- y is the last
apprev xs    []   =             xs

(so that, actually, apprev == flip append! -- i.e. we're basically just looking at the same thing in a mirror.)

A type is defined by its interactions, not representation. There's no need for a completely new data type definition if the old one is exactly isomorphic to the new (can represent it just fine). Just a newtype tag will suffice, to signal the different behavior on appends:

newtype RevList a  =  Rev [a]

instance Semigroup (RevList a) where
   Rev xs <> Rev []      =  Rev xs
   Rev xs <> Rev (y:ys)  =  Rev (y:zs) 
                              where 
                              Rev zs  =  Rev xs <> Rev ys

(a side note: your definition does not handle all possible cases. it also mixes Nil and RNil up.)