Making a basic Haskell type an instance of a new typeclass

Pierre 08/23/2018. 6 answers, 303 views

Say I'm trying to define a new typeclass in Haskell, and among other things, it must have a + operation:

class Foo a where
    (+) :: a -> a -> a

(In practice the typeclass Foo would have more stuff, but let me stop here to keep this minimal.)

Now, I want to make the basic "Integer" type an instance of Foo; and as far as the + operation goes, I just want to keep the usual addition, which is already defined. How do I do that?

Needless to say, the following makes no sense:

instance Foo Integer where
    (+) x y = x+y

It loops forever when I ask Haskell to compute 2+3, but I suppose that's to be expected! I also tried putting nothing at all:

instance Foo Integer

This compiles, but then when I ask for 2+3 I get "No instance nor default method for class operation +". Again, it makes sense...

But how do we do that then?

I guess it's a general question "about the namespace", I mean, when two typeclasses use the same names, what happens? In my case, I get issues when trying to make a type (Integer) an instance of two typeclasses with such a name clash (Num and Foo).

From reading this question, I'm now afraid that what I'm asking for is just prohibited...

6 Answers

Tanner Swett 08/23/2018.

If you really want to call your operation +, and you want to define it in terms of the + from Prelude, you can do it like this:

import Prelude (Integer, print)
import qualified Prelude ((+))

class Foo a where
  (+) :: a -> a -> a

instance Foo Integer where
  (+) x y = x Prelude.+ y

main = print (2 + 3 :: Integer)

This will output 5.

To prove that the definition of main is really using our new + operator and not the original one, we can change our definition of +:

import Prelude (Integer, print)
import qualified Prelude ((+))

class Foo a where
  (+) :: a -> a -> a

instance Foo Integer where
  (+) x y = x Prelude.+ y Prelude.+ 1

main = print (2 + 3 :: Integer)

This will output 6.

Samuel Barr 08/23/2018.

To give a specific solution, something like this would work without having to deal with clashing with (+):

class Foo a where
  (<+>) :: a -> a -> a

infixl 6 <+>

instance Foo Integer where
  (<+>) = (+)

Now your class Foo has its own operator, and the fixity declaration means that (<+>) will be parsed the same way as (+).

Adam Smith 08/23/2018.

I don't think you'd do this the same way in Haskell as in a pure math setting. You're trying to implement an Abelian group (it seems from your comments), which I understand (as someone who hasn't taken a mathematics course since High School) to be a group of type a where there exists a function f :: a -> a -> a such that f x y = f y x.

Contrast that with Haskells built-in (and oft-used) Monoid class and you'll see why I say Haskellers may approach this differently. Monoids are a group of type a where there exists a function f :: a -> a -> a such that f x (f y z) = f (f x y) z (and additionally, but unrelated, a value k such that f x k = x). It's representing associativity rather than commutativity, but is otherwise identical.

Monoids are represented as such:

class Monoid a where
  mempty :: a
  mappend :: a -> a -> a
  mconcat :: [a] -> a

and several are defined, like Sum

newtype Sum a = Sum { getSum :: a }
instance Num a => Monoid (Sum a) where
  mempty = 0
  mappend (Sum x) (Sum y) = Sum (x+y)
  -- mconcat is defined as `foldr mappend mempty`

Note that it doesn't try to redefine (+) here. In fact it defines its own operator as a synonym for mappend: (<>). I recommend using a similar syntax with your Abelian group.

class Abelian a where
  (|<>|) :: a -> a -> a  -- or similar

bergey 08/23/2018.

As others have answered, the easiest option is to pick a new name for the operator. This is the approach that most existing packages take, so that users of the library can continue using Num as usual.

As the OP notes in the comments, it is also possible to import Prelude qualified, and define + however you want. Any other modules that want to use your new + will need to do the same. They can either not import Prelude (via import Prelude () or NoImplicitPrelude), or import it qualified. This is pretty reasonable in a large codebase, where everyone knows the local convention and you can provide instances for all the types you use.

Alexey Romanov 08/23/2018.

In Haskell, you can use your own Prelude instead of the standard one, and there are multiple existing alternative preludes. E.g. numeric-prelude defines (+) in Algebra.Additive.C.

Ignat Insarov 08/24/2018.

a programmer's approach   As others have said, it is probably best if you forfeit the convenient notation for additive and multiplicative monoids that is widely used in the mathematics community, and rather think of monoid in a more abstract fashion — as a distinct algebraic structure with a distinct operation that, generally, has nothing to do with addition and multiplication in number rings, of which the Num typeclass may be considered an approximate formalization. The Haskell type system is relying on you to make sure that same names refer to same things, and distinct names refer to distinct ones — this simple and logical rule helps it avoid confusion. This is why the monoidal operation of any monoid is usually referred to with a lozenge <> sign. (Actually, the operation on initial monoids is sometimes denoted ++, because of tradition.)

It is also somewhat illogical, but considered practical, that Num for usual numbers is defined first (and even on the hardware level), and the constituting monoids are extracted afterwards:


198 instance Num a => Semigroup (Sum a) where                                                            
199         (<>) = coerce ((+) :: a -> a -> a)                                                           
227 instance Num a => Semigroup (Product a) where
228         (<>) = coerce ((*) :: a -> a -> a)

— So it happens that + gets occupied long before any abstract algebra comes into play.


an algebraist's approach   Nevertheless, it is very much possible to define a ring out of a type with two monoids:

{-# language FlexibleInstances #-}
{-# language FlexibleContexts #-}
{-# language UndecidableInstances #-}
{-# language TypeApplications #-}

module MonoidsField where

import Prelude (Integer, Semigroup, (<>), Monoid, mempty, Show, show)
import Data.Coerce

newtype Sum a = Sum { getSum :: a }

newtype Product a = Product { getProduct :: a }

data N = Z | S { predecessor :: N}

-- | Substract one; decrease.
dec :: Coercible a (N -> N) => a
dec = coerce predecessor

instance Show N
    show Z = ""
    show x = '|' : show (predecessor x)

instance Semigroup (Sum N)
    u <> (Sum Z) = u
    u <> v = coerce S (u <> dec v)

instance Monoid (Sum N)
    mempty = Sum Z

instance Semigroup (Product N)
    u <> (Product Z) = coerce (mempty @(Sum N))
    u <> v = let (*) =         (<>) @(Product N)
                 (+) = coerce ((<>) @(Sum N))
             in u + (u * dec v)

instance Monoid (Product N)
    mempty = Product (S Z)

(+) :: Monoid (Sum a) => a -> a -> a
x + y = getSum (Sum x <> Sum y)

(*) :: Monoid (Product a) => a -> a -> a
x * y = getProduct (Product x <> Product y)

class PseudoRing a

instance (Monoid (Sum a), Monoid (Product a)) => PseudoRing a
    -- You may add some functions that make use of distributivity between the additive and
    -- multiplicative monoid.

-- ^
-- λ (S (S (S Z))) + (S (S Z))
-- |||||
-- λ (S (S (S Z))) * (S (S Z))
-- ||||||

— As you see, the PseudoRing class does not add any operations by itself, but it does make sure that the two monoids it requires are defined. If you instantiate it, it is implied that you have ensured that the axiom of distribution holds.

As this example suggests, a subclass may be thought of as including in itself all the operations defined for its superclasses. So, it is a possibility that you proclaim instance Num a => Foo a and get to re-use the definition of +. You may then go as far as to define "partial" instances of Num for your own types that a priory will not have a definition for all the required methods. This approach is obviously unsafe, confusing and overall not advisable, but it may be just what you need, especially if decorated with an appropriate scientific license. So, your example becomes:

class Foo a

instance Num a => Foo a


Let me know if any of the above requires clarification! - Download Hi-Res Songs

1 Alan Walker

Different World flac

Alan Walker. 2018. Writer: Shy Nodi;Alan Walker;Fredrik Borch Olsen;James Njie;Marcus Arnbekk;Gunnar Greve Pettersen;K-391;Corsak;Shy Martin;Magnus Bertelsen.
2 Skylar Grey

Everything I Need flac

Skylar Grey. 2018.

Tell Me It's Over

4 Ariana Grande

​Thank U, Next flac

Ariana Grande. 2018. Writer: Crazy Mike;Scootie;Victoria Monét;Tayla Parx;TBHits;Ariana Grande.
5 Julian Jordan

Glitch flac

Julian Jordan. 2018.
6 Mesto

Wait Another Day flac

Mesto. 2018.
7 The Chainsmokers

Hope flac

The Chainsmokers. 2018. Writer: Kate Morgan;Chris Lyon;Alex Pall;Andrew Taggart.
8 Anne-Marie

Rewrite The Stars flac

Anne-Marie. 2018. Writer: Benj Pasek;Justin Paul.
9 Ariana Grande

Imagine flac

Ariana Grande. 2018. Writer: Jameel Roberts;Priscilla Renea;Happy Perez;Andrew "Pop" Wansel;Ariana Grande.
10 Rita Ora

Let You Love Me flac

Rita Ora. 2018. Writer: Rita Ora;Easyfun;Fred Gibson;Noonie Bao;LotusIV;Ilsey Juber.
11 Alan Walker

Lily flac

Alan Walker. 2018.
12 Alan Walker

Lost Control flac

Alan Walker. 2018.
13 Emma Hewitt

Take Everything flac

Emma Hewitt. 2018. Writer: G. Emery;E. Hewitt;A. Hewitt.
14 Conor Maynard

How You Love Me flac

Conor Maynard. 2018. Writer: Yoshi Breen;Thom Bridges;Hardwell;Rik Annema;Conor Maynard;Cimo Fränkel;Snoop Dogg.

There You Are flac

ZAYN. 2018. Writer: Joe Garrett;Levi Lennox;Michael Hannides;Anthony Hannides;ZAYN.
16 Pollyanna

Starchild flac

Pollyanna. 2018.
17 (G)I-DLE


(G)I-DLE. 2018. Writer: Riot Music Team;Harloe.
18 Fitz And The Tantrums

HandClap flac

Fitz And The Tantrums. 2017. Writer: Fitz And The Tantrums;Eric Frederic;Sam Hollander.
19 Mark Ronson

Nothing Breaks Like A Heart flac

Mark Ronson. 2018. Writer: Thomas Brenneck;Maxime Picard;Ilsey Juber;Conor Szymanski;Clement Picard;Mark Ronson;Miley Cyrus.
20 Alan Walker

I Don't Wanna Go flac

Alan Walker. 2018.

Related questions

Hot questions


Popular Tags