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 AJR

Birthday Party flac

AJR. 2019. Writer: Adam Met;Jack Met;Ryan Met;Peter Ivers;David Lynch.
2 Loote

Your Side Of The Bed flac

Loote. 2018. Writer: ​Jesse Saint John;Jackson Foote;Emma Lov Block.
3 AJR

100 Bad Days flac

AJR. 2019. Writer: Jack Met;Adam Met;Ryan Met.
4 Joe Jonas

Longer Than I Thought flac

Joe Jonas. 2018. Writer: Patrick Nissley;Jackson Foote;Dave Katz.
5 Loote

Out Of My Head flac

Loote. 2018. Writer: Emma Lov Block;Michael Pollack;Jeremy Dussolliet;Jackson Foote.
6 Iselin Solheim

Anyone Out There flac

Iselin Solheim. 2019. Writer: Iselin Solheim;Max Grahn.
7 Loote

Wish I Never Met You flac

Loote. 2018. Writer: Jackson Foote;Alex Peter Koste;Jeremy Dussolliet;Emma Lov Block.
8 Kim Petras

Heart To Break flac

Kim Petras. 2018. Writer: Cirkut;Aaron Joseph;Dr. Luke;Jacob Kasher;Kim Petras.
9 A L E X

Out On The Trampoline At Night flac

A L E X. 2018. Writer: A L E X.
10 A L E X

I Want To Hold Your Hand flac

A L E X. 2018. Writer: A L E X.
11 A L E X

Field flac

A L E X. 2018. Writer: A L E X.
12 A L E X

Save Me flac

A L E X. 2018. Writer: A L E X.
13 Devin

Summer Lover flac

Devin. 2019. Writer: Tommy Lee James;Stuart Crichton;Oliver Heldens;Nile Rodgers;Devin Guisande.
14 A L E X

9 To 5 flac

A L E X. 2018. Writer: A L E X.
15 A L E X

Skirt flac

A L E X. 2018. Writer: A L E X.
16 Florian Picasso

Midnight Sun (Extended Version) flac

Florian Picasso. 2019.
17 Florian Picasso

Midnight Sun flac

Florian Picasso. 2019.
18 21 Savage

Enzo flac

21 Savage. 2019. Writer: YungLunchBox;Sheck Wes;Offset;Gucci Mane;21 Savage;DJ Snake.
19 Tales Of Ratatösk

Battle Of The Doomed Gods 320kbps

Tales Of Ratatösk. 2019.
20 Tales Of Ratatösk

Andro 320kbps

Tales Of Ratatösk. 2019.

Related questions

Hot questions

Language

Popular Tags