Making a basic Haskell type an instance of a new typeclass

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

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:

base-4.11.1.0:Data.Semigroup.Internal

...
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
  where
    show Z = ""
    show x = '|' : show (predecessor x)

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

instance Monoid (Sum N)
  where
    mempty = Sum Z

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

instance Monoid (Product N)
  where
    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
  where
    -- 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!


HighResolutionMusic.com - Download Hi-Res Songs

1 Elohim

Holding Hands flac

Elohim. 2019. Writer: Quinn XCII;Jeremy Zucker;Elohim;Danny Parra;Corey Berkowitz;Antonina Armato.
2 P!nk

Walk Me Home flac

P!nk. 2019. Writer: P!nk;Scott Harris;Nate Ruess.
3 Katy Perry

365 flac

Katy Perry. 2019. Writer: Zedd;Katy Perry;Caroline Ailin;Corey Sanders;Daniel Davidsen;Cutfather;Peter Wallevik.
4 DEAMN

Happy flac

DEAMN. 2019.
5 Ariana Grande

7 Rings flac

Ariana Grande. 2019. Writer: Ariana Grande;Richard Rodgers;TBHits;Njomza;Michael "Mikey" Foster;Kaydence;Tayla Parx;Scootie;Oscar Hammerstein II;Victoria Monét.
6 5 Seconds Of Summer

Who Do You Love flac

5 Seconds Of Summer. 2019. Writer: Andrew Taggart;Talay Riley;Oak;Sean Douglas;Luke Hemmings;Calum Hood;Ashton Irwin;Michael Clifford;Trevorious;Zaire Koalo.
7 Bonn

No Sleep flac

Bonn. 2019. Writer: Albin Nedler;Bonn;Martin Garrix.
8 Bruno Mars

Please Me flac

Bruno Mars. 2019. Writer: J. White Did It;DJ White Shadow;Philip Lawrence;Bruno Mars;Cardi B.
9 Brooks

Better When You're Gone flac

Brooks. 2019. Writer: David Guetta;Emma Lov Block;Ido Zmishlany;Jackson Foote;Jeremy Dussolliet;Brooks.
10 Ariana Grande

Imagine flac

Ariana Grande. 2019. Writer: JProof;Priscilla Renea;Happy Perez;Andrew "Pop" Wansel;Ariana Grande.
11 Avril Lavigne

Dumb Blonde flac

Avril Lavigne. 2019. Writer: Mitch Allan;Bonnie McKee;Nicki Minaj;Avril Lavigne.
12 Avril Lavigne

Birdie flac

Avril Lavigne. 2019. Writer: J.R. Rotem;Avril Lavigne.
13 Ariana Grande

Thank U, Next flac

Ariana Grande. 2019. Writer: Crazy Mike;Scootie;Victoria Monét;Tayla Parx;TBHits;Ariana Grande.
14 Ariana Grande

Fake Smile flac

Ariana Grande. 2019. Writer: Joseph Frierson;Mary Frierson;Fred Ball;Happy Perez;Andrew "Pop" Wansel;Wendy Rene;Kennedi Lykken;Priscilla Renea;Justin Tranter;Ariana Grande.
15 Avril Lavigne

Warrior flac

Avril Lavigne. 2019. Writer: Chad Kroeger;Travis Clark;Chris Baseford;Avril Lavigne.
16 Nichkhun

Home flac

Nichkhun. 2019.
17 Dua Lipa

Swan Song flac

Dua Lipa. 2019. Writer: Dua Lipa;Justin Tranter;Kennedi Lykken;Mattias Larsson;Robin Fredriksson;Junkie XL.
18 Dimitri Vegas & Like Mike

Selfish flac

Dimitri Vegas & Like Mike. 2019. Writer: Victor Thell;Michael Thivaios;Maria Smith;Jeff Porcaro;Dimitri Thivaios;David Paich;Dan Book;Will Grands;ANGEMI.
19 Jason Derulo

Let's Shut Up And Dance flac

Jason Derulo. 2019. Writer: Bongo.
20 Ariana Grande

Needy flac

Ariana Grande. 2019. Writer: Tayla Parx;TBHits;Victoria Monét;Ariana Grande.

Related questions

Hot questions

Language

Popular Tags