Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Why is (+), (-) part of Num typeclass?

From ghci:

Prelude> :i Num                              
class Num a where                            
  (+) :: a -> a -> a                         
  (-) :: a -> a -> a                         
  (*) :: a -> a -> a                         
  negate :: a -> a                           
  abs :: a -> a                              
  signum :: a -> a                           
  fromInteger :: Integer -> a                
        -- Defined in `GHC.Num'              
instance Num Word -- Defined in `GHC.Num'    
instance Num Integer -- Defined in `GHC.Num' 
instance Num Int -- Defined in `GHC.Num'     
instance Num Float -- Defined in `GHC.Float' 
instance Num Double -- Defined in `GHC.Float'

Why is (+), (-) part of the Num class to begin with?

For example - you could easily define this type class:

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

And then let:

instance Plus [] where
    (+) = (++)

And you could also define these for sets to mean set union, or add (-) to a type class to mean set difference... And it makes no sense to define signum on a list.

Of course I could create my own type class that uses (|+|) instead - but why was these operators reserved in haskell for Num alone?

So why was this choice made? Is it due to legacy or has no one wanted this?

like image 756
Centril Avatar asked Jan 13 '16 02:01

Centril


2 Answers

My guess is that they intended Num to reflect the mathematical nature of numbers, rather than some more general structure. The operation in Num are largely those that support an abstract mathematical structure called a ring, of which the integers (and any extensions thereof) are a prominent example.

Since Haskell allows you to define your own operators, you can set up your own notation. Then, when others read your code, they will be able to tell that you are working with something which is not Number-like, but perhaps more general.

There are many unfortunate historical design decisions in the Haskell prelude, which cannot be fixed because they would break compatibility. However, IMHO, the basic design of the Num typeclass and operators is sound.

like image 25
comingstorm Avatar answered Oct 23 '22 04:10

comingstorm


A lot of it is due to historic reasons, but there are also mathematical ones. For example, there are already mathematical names for structures that support binary operators. The most commonly used one is a Monoid, which you can use in Data.Monoid. This typeclass defines a function mappend and a value mempty, the equivalent of the identity element for mappend, and there is an operator alias for mappend called <>. Lists and many other objects form monoids, and numbers actually form 2, with + and with *, where the identity elements are 0 and 1 respectively. Structures that have an identity, an associative binary operation, and an inverse for that operation (subtraction is the inverse of addition, for example) are called groups (not part of the standard library), and structures that form a group under one operator and a monoid under a second operator are called rings. These objects are the basis for algebraic structures/abstract algebra classes.

These mathematical constructs are somewhat tricky to implement in Haskell, at least very well. There are overlapping instances for Monoid for all Num types with + and *, and for some numeric types there's a Group overlap if / can be defined such that dividing by 0 is well defined (some structures can allow this). These overlapping instances lead to lots of newtypes that make it difficult to work on a day to day basis. The Num typeclass helps here because it provides a useful interface, that of manipulating and performing operations on numbers, that is easy to use in real world code rather than just in academia. There have been attempts to introduce a more mathematical version of Prelude, and some people use these with varying success, but your average Haskeller would rather ditch mathematical purity for a more practical interface.

In short, the Num typeclass is defined this way for historical reasons, but also for very practical reasons. A more strict mathematical construction is unwieldy, and for many types simply using Data.Monoid's <> operator is perfect.

like image 192
bheklilr Avatar answered Oct 23 '22 05:10

bheklilr