Making a basic Haskell type an instance of a new typeclass
up vote
8
down vote
favorite
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...
haskell
|
show 5 more comments
up vote
8
down vote
favorite
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...
haskell
I'm realizing that I could do this within a module, with "import qualified Prelude" at the start, and then "instance Foo Prelude.Integer where (+) x y = x Prelude.+ y" or something... right? and then if I import my newly created module, does it all work? how does Haskell understand "2+3" ?
– Pierre
Aug 23 at 15:58
5
Right, I think you're barking up the wrong tree. As you mentioned -- you could ìmport qualified Prelude` and write every addition symbol asPrelude.+, but that seems...wildly unnecessary. Why doesFoohave to use the+operator? If you simply change it to something unique, all this craziness goes away :)
– Adam Smith
Aug 23 at 16:07
@Adam: to take a very mathematical example (but i am, after all, a working mathematician...), you could imagine that Foo is really called AbelianGroup, so that the elements of a type of this class can be added, but NOT multiplied, so they don't belong to Num. Yet it is very natural to use + and not some other symbol... while elements of type "Integer" are certainly examples, among others.
– Pierre
Aug 23 at 16:23
1
PS you could rephrase my question as: how do you define a weaker version of Num, or of any typeclass, where some functions are not defined?
– Pierre
Aug 23 at 16:25
2
There is hackage.haskell.org/package/groups-0.4.1.0/docs/Data-Group.html, which builds groups and abelian groups on top of monoid, using the <> operator. Also- Num has no superclasses (it doesn't require Show or Eq)
– Samuel Barr
Aug 23 at 17:00
|
show 5 more comments
up vote
8
down vote
favorite
up vote
8
down vote
favorite
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...
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...
haskell
haskell
edited Aug 23 at 21:25
Peter Mortensen
13.4k1983111
13.4k1983111
asked Aug 23 at 15:49
Pierre
1485
1485
I'm realizing that I could do this within a module, with "import qualified Prelude" at the start, and then "instance Foo Prelude.Integer where (+) x y = x Prelude.+ y" or something... right? and then if I import my newly created module, does it all work? how does Haskell understand "2+3" ?
– Pierre
Aug 23 at 15:58
5
Right, I think you're barking up the wrong tree. As you mentioned -- you could ìmport qualified Prelude` and write every addition symbol asPrelude.+, but that seems...wildly unnecessary. Why doesFoohave to use the+operator? If you simply change it to something unique, all this craziness goes away :)
– Adam Smith
Aug 23 at 16:07
@Adam: to take a very mathematical example (but i am, after all, a working mathematician...), you could imagine that Foo is really called AbelianGroup, so that the elements of a type of this class can be added, but NOT multiplied, so they don't belong to Num. Yet it is very natural to use + and not some other symbol... while elements of type "Integer" are certainly examples, among others.
– Pierre
Aug 23 at 16:23
1
PS you could rephrase my question as: how do you define a weaker version of Num, or of any typeclass, where some functions are not defined?
– Pierre
Aug 23 at 16:25
2
There is hackage.haskell.org/package/groups-0.4.1.0/docs/Data-Group.html, which builds groups and abelian groups on top of monoid, using the <> operator. Also- Num has no superclasses (it doesn't require Show or Eq)
– Samuel Barr
Aug 23 at 17:00
|
show 5 more comments
I'm realizing that I could do this within a module, with "import qualified Prelude" at the start, and then "instance Foo Prelude.Integer where (+) x y = x Prelude.+ y" or something... right? and then if I import my newly created module, does it all work? how does Haskell understand "2+3" ?
– Pierre
Aug 23 at 15:58
5
Right, I think you're barking up the wrong tree. As you mentioned -- you could ìmport qualified Prelude` and write every addition symbol asPrelude.+, but that seems...wildly unnecessary. Why doesFoohave to use the+operator? If you simply change it to something unique, all this craziness goes away :)
– Adam Smith
Aug 23 at 16:07
@Adam: to take a very mathematical example (but i am, after all, a working mathematician...), you could imagine that Foo is really called AbelianGroup, so that the elements of a type of this class can be added, but NOT multiplied, so they don't belong to Num. Yet it is very natural to use + and not some other symbol... while elements of type "Integer" are certainly examples, among others.
– Pierre
Aug 23 at 16:23
1
PS you could rephrase my question as: how do you define a weaker version of Num, or of any typeclass, where some functions are not defined?
– Pierre
Aug 23 at 16:25
2
There is hackage.haskell.org/package/groups-0.4.1.0/docs/Data-Group.html, which builds groups and abelian groups on top of monoid, using the <> operator. Also- Num has no superclasses (it doesn't require Show or Eq)
– Samuel Barr
Aug 23 at 17:00
I'm realizing that I could do this within a module, with "import qualified Prelude" at the start, and then "instance Foo Prelude.Integer where (+) x y = x Prelude.+ y" or something... right? and then if I import my newly created module, does it all work? how does Haskell understand "2+3" ?
– Pierre
Aug 23 at 15:58
I'm realizing that I could do this within a module, with "import qualified Prelude" at the start, and then "instance Foo Prelude.Integer where (+) x y = x Prelude.+ y" or something... right? and then if I import my newly created module, does it all work? how does Haskell understand "2+3" ?
– Pierre
Aug 23 at 15:58
5
5
Right, I think you're barking up the wrong tree. As you mentioned -- you could ìmport qualified Prelude` and write every addition symbol as
Prelude.+, but that seems...wildly unnecessary. Why does Foo have to use the + operator? If you simply change it to something unique, all this craziness goes away :)– Adam Smith
Aug 23 at 16:07
Right, I think you're barking up the wrong tree. As you mentioned -- you could ìmport qualified Prelude` and write every addition symbol as
Prelude.+, but that seems...wildly unnecessary. Why does Foo have to use the + operator? If you simply change it to something unique, all this craziness goes away :)– Adam Smith
Aug 23 at 16:07
@Adam: to take a very mathematical example (but i am, after all, a working mathematician...), you could imagine that Foo is really called AbelianGroup, so that the elements of a type of this class can be added, but NOT multiplied, so they don't belong to Num. Yet it is very natural to use + and not some other symbol... while elements of type "Integer" are certainly examples, among others.
– Pierre
Aug 23 at 16:23
@Adam: to take a very mathematical example (but i am, after all, a working mathematician...), you could imagine that Foo is really called AbelianGroup, so that the elements of a type of this class can be added, but NOT multiplied, so they don't belong to Num. Yet it is very natural to use + and not some other symbol... while elements of type "Integer" are certainly examples, among others.
– Pierre
Aug 23 at 16:23
1
1
PS you could rephrase my question as: how do you define a weaker version of Num, or of any typeclass, where some functions are not defined?
– Pierre
Aug 23 at 16:25
PS you could rephrase my question as: how do you define a weaker version of Num, or of any typeclass, where some functions are not defined?
– Pierre
Aug 23 at 16:25
2
2
There is hackage.haskell.org/package/groups-0.4.1.0/docs/Data-Group.html, which builds groups and abelian groups on top of monoid, using the <> operator. Also- Num has no superclasses (it doesn't require Show or Eq)
– Samuel Barr
Aug 23 at 17:00
There is hackage.haskell.org/package/groups-0.4.1.0/docs/Data-Group.html, which builds groups and abelian groups on top of monoid, using the <> operator. Also- Num has no superclasses (it doesn't require Show or Eq)
– Samuel Barr
Aug 23 at 17:00
|
show 5 more comments
6 Answers
6
active
oldest
votes
up vote
4
down vote
accepted
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.
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
add a comment |
up vote
8
down vote
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 (+).
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
add a comment |
up vote
2
down vote
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
add a comment |
up vote
1
down vote
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.
add a comment |
up vote
1
down vote
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.
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
add a comment |
up vote
1
down vote
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!
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
just use-# LANGUAGE RebindableSyntax #-followed byimport qualified Preludeand thenfromInteger :: Integer -> Integerwith finallyfromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)
– Pierre
Aug 29 at 22:33
add a comment |
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
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.
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
add a comment |
up vote
4
down vote
accepted
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.
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
add a comment |
up vote
4
down vote
accepted
up vote
4
down vote
accepted
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.
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.
answered Aug 23 at 21:38
Tanner Swett
1,5411325
1,5411325
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
add a comment |
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
As you can see from the comments, this is what I had eventually in mind. Because this is definitely the right answer to my specific question, I'll accept it.
– Pierre
Aug 24 at 7:31
add a comment |
up vote
8
down vote
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 (+).
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
add a comment |
up vote
8
down vote
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 (+).
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
add a comment |
up vote
8
down vote
up vote
8
down vote
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 (+).
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 (+).
answered Aug 23 at 16:21
Samuel Barr
1497
1497
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
add a comment |
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"parsed the same way as (+)", in what sense is it the same way? (do you just mean "with the same associativity properties" ?) Anyway, I really need (want...) to use +...
– Pierre
Aug 23 at 16:31
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
"infixl 6" means that (<+>) is left associative with a precedence of 6 (in haskell, operators can have a precedence from 0-9, with 0 being lowest and 9 being highest). Left associative means that if you have an expression 1 <+> 2 <+> 3 <+> 4 it will be parsed as (((1 <+> 2) <+> 3) <+> 4); the operations will be performed from left to right. This is the same way (+) behaves.
– Samuel Barr
Aug 23 at 16:35
add a comment |
up vote
2
down vote
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
add a comment |
up vote
2
down vote
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
add a comment |
up vote
2
down vote
up vote
2
down vote
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
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
answered Aug 23 at 17:03
Adam Smith
32.8k53174
32.8k53174
add a comment |
add a comment |
up vote
1
down vote
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.
add a comment |
up vote
1
down vote
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.
add a comment |
up vote
1
down vote
up vote
1
down vote
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.
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.
answered Aug 23 at 17:14
bergey
2,303515
2,303515
add a comment |
add a comment |
up vote
1
down vote
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.
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
add a comment |
up vote
1
down vote
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.
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
add a comment |
up vote
1
down vote
up vote
1
down vote
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.
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.
answered Aug 23 at 19:40
Alexey Romanov
105k25207348
105k25207348
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
add a comment |
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
absolutely! NumericPrelude seems great. But I was wondering if you could achieve some of its functionality in an elementary way (re-writing the Prelude is far beyond what I can personally do, and at the same time, I'd like to understand what I use, in this case).
– Pierre
Aug 24 at 7:30
add a comment |
up vote
1
down vote
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!
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
just use-# LANGUAGE RebindableSyntax #-followed byimport qualified Preludeand thenfromInteger :: Integer -> Integerwith finallyfromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)
– Pierre
Aug 29 at 22:33
add a comment |
up vote
1
down vote
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!
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
just use-# LANGUAGE RebindableSyntax #-followed byimport qualified Preludeand thenfromInteger :: Integer -> Integerwith finallyfromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)
– Pierre
Aug 29 at 22:33
add a comment |
up vote
1
down vote
up vote
1
down vote
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!
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!
edited Aug 24 at 10:17
answered Aug 24 at 8:42
Ignat Insarov
1,7501023
1,7501023
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
just use-# LANGUAGE RebindableSyntax #-followed byimport qualified Preludeand thenfromInteger :: Integer -> Integerwith finallyfromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)
– Pierre
Aug 29 at 22:33
add a comment |
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
just use-# LANGUAGE RebindableSyntax #-followed byimport qualified Preludeand thenfromInteger :: Integer -> Integerwith finallyfromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)
– Pierre
Aug 29 at 22:33
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
Thanks, very interesting. By the way, ultimately my solution will be to use -XRebindableSyntax. Not only does this NOT load the prelude, so I'll get rid of Num entirely; but also, one can now redefine fromInteger (to be of type Integer -> Integer and equal to Prelude.id), so that literals are interpreted as "Integer" and not Num a => a. So Num is entirely gone from my life, and there are now plenty of available solutions.
– Pierre
Aug 28 at 8:57
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
@Pierre Would the source code be available? I am keen on taking a look.
– Ignat Insarov
Aug 28 at 15:17
just use
-# LANGUAGE RebindableSyntax #-followed by import qualified Prelude and then fromInteger :: Integer -> Integer with finally fromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)– Pierre
Aug 29 at 22:33
just use
-# LANGUAGE RebindableSyntax #-followed by import qualified Prelude and then fromInteger :: Integer -> Integer with finally fromInteger = Prelude.id. Then as in the accepted answer, for example (or whatever else you want to do without Num!). I found the trick as an answer to a question on this site :-)– Pierre
Aug 29 at 22:33
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51989707%2fmaking-a-basic-haskell-type-an-instance-of-a-new-typeclass%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown


I'm realizing that I could do this within a module, with "import qualified Prelude" at the start, and then "instance Foo Prelude.Integer where (+) x y = x Prelude.+ y" or something... right? and then if I import my newly created module, does it all work? how does Haskell understand "2+3" ?
– Pierre
Aug 23 at 15:58
5
Right, I think you're barking up the wrong tree. As you mentioned -- you could ìmport qualified Prelude` and write every addition symbol as
Prelude.+, but that seems...wildly unnecessary. Why doesFoohave to use the+operator? If you simply change it to something unique, all this craziness goes away :)– Adam Smith
Aug 23 at 16:07
@Adam: to take a very mathematical example (but i am, after all, a working mathematician...), you could imagine that Foo is really called AbelianGroup, so that the elements of a type of this class can be added, but NOT multiplied, so they don't belong to Num. Yet it is very natural to use + and not some other symbol... while elements of type "Integer" are certainly examples, among others.
– Pierre
Aug 23 at 16:23
1
PS you could rephrase my question as: how do you define a weaker version of Num, or of any typeclass, where some functions are not defined?
– Pierre
Aug 23 at 16:25
2
There is hackage.haskell.org/package/groups-0.4.1.0/docs/Data-Group.html, which builds groups and abelian groups on top of monoid, using the <> operator. Also- Num has no superclasses (it doesn't require Show or Eq)
– Samuel Barr
Aug 23 at 17:00