numhask-0.7.1.0: A numeric class hierarchy.
Safe HaskellNone
LanguageHaskell2010

NumHask

Description

Numeric classes.

Synopsis

Usage

>>> :set -XRebindableSyntax
>>> :set -XNegativeLiterals
>>> import NumHask.Prelude
>>> 1+1
2

Overview

numhask is largely a set of classes that can replace the Num class and it's descendents. Principles that have guided design include:

  • balanced class density. The numeric heirarchy begins with addition and multiplication, choosing not to build from a Magma base. Whilst not being as principled as other approaches, this circumvents the instance explosion problems of Haskell whilst maintaining clarity of class purpose.
  • operator-first. In most cases, a class exists to define useful operators. The exceptions are Distributive, Ring and Field, which are collections of operators representing major teleological fault lines.
  • lawful. Most classes have laws associated with them that serve to relate class operators together in a meaningful way.
  • low-impact. The library attempts to fit in with the rest of the Haskell ecosystem. It provides instances for common numbers: Int, Integer, Double, Float and the Word classes. It avoids name (or idea) clashes with other popular libraries and adopts conventions in the current prelude where they make sense.
  • proof-of-concept. The library may be below industrial-strength depending on a definition of this term. At the same time, correspondence around improving the library is most welcome.

The class heirarchy looks somewhat like this:

If the base started with magma, and the library tolerated clashing with Semigroup and Monoid in base, it would look like:

These first two levels, contained in Group can be considered "morally" super-classes.

Prelude Mappings

Num is a very old part of haskell, and is virtually unchanged since it's specification in haskell98.

A deconstruction of Num and mapping to numhask.

-- | Basic numeric class.
class  Num a  where
   {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}

   (+), (-), (*)       :: a -> a -> a
   -- | Unary negation.
   negate              :: a -> a

(+) is an operator of the Additive class

(-) & negate are functions in the Subtractive class, and

(*) is an operator of the Multiplicative class.

zero and one are also introduced to the numeric heirarchy.

   -- | Absolute value.
   abs                 :: a -> a
   -- | Sign of a number.
   -- The functions 'abs' and 'signum' should satisfy the law:
   --
   -- > abs x * signum x == x
   --
   -- For real numbers, the 'signum' is either @-1@ (negative), @0@ (zero)
   -- or @1@ (positive).
   signum              :: a -> a

abs is a function in the Signed class. The concept of an absolute value can also include situations where the domain and codomain are different, and norm as a function in the Norm class is supplied for these cases.

sign replaces signum, because signum is simply a naming crime. basis can also be seen as a generalisation of sign.

   -- | Conversion from an 'Integer'.
   -- An integer literal represents the application of the function
   -- 'fromInteger' to the appropriate value of type 'Integer',
   -- so such literals have type @('Num' a) => a@.
   fromInteger         :: Integer -> a

FromInteger becomes its own class and FromIntegral is introduced to polymorphise the covariant.

Mappings from other areas of prelude include:\

Integral becomes Integral and a polymorphic ToIntegral is introduced.

Fractional is roughly synonymous to Field together with a polymorphic FromRatio.

RealFrac becomes the polymorphic QuotientField

Floating is split into ExpField and TrigField

RealFloat is not attempted. Life is too short.

NumHask imports protolude as a base prelude with some minor tweaks.

Extensions

RebindableSyntax and NegativeLiterals are both recommended for use with numhask. LexicalNegation also looks sweet when it arrives.

As a replacement for the numerical classes, numhask clashes significantly with an unqualified import of the Prelude. Either numhask modules should be qualified, or prelude turned off with the NoImplicitPrelude extension, or with RebindableSyntax, which implies NoImplicitPrelude.

defaulting

Without RebindableSyntax, numeric literals default as follows:

>>> :set -XNoRebindableSyntax
>>> :t 1
1 :: Num p => p
>>> :t 1.0
1.0 :: Fractional p => p

With RebindableSyntax (which also switches NoImplicitPrelude on) literal numbers change to the numhask types, FromInteger and FromRational:

>>> :set -XRebindableSyntax
>>> :t 1
1 :: FromInteger a => a
>>> :t 1.0
1.0 :: FromRational a => a
>>> 1
1
>>> 1.0
1.0

It is recommended to switch on RebindableSyntax to avoid Num constraints being introduced due to literal defaulting. The extension is a tradeoff, however, and usage comes attached with other non-numeric changes that NumHask.Prelude attempts to counteract.

See See haskell2010 Section 4.3.4 for the nuts and bolts to defaulting.

The effect of ExtendedDefaultRules in ghci or switched on as an extension also need to be understood. It can lead to unusual interactions with numerics and strange error messages at times because it adds () and [] to the start of the type defaulting list.

Negatives

Without NegativeLiterals, GHC and Haskell often reads a negative as subtraction rather than a minus.

:set -XNoNegativeLiterals
:t Point 1 -2

Point 1 -2 :: (Subtractive (Point a), FromInteger a, FromInteger (a -> Point a)) => a -> Pair a ...

:set -XNegativeLiterals
:t Point 1 -2

Point 1 -2 :: FromInteger a => Point a

Point 1 -2

Point 1 -2

LexicalNegation is coming soon as a valid replacement for NegativeLiterals and will tighten things up further.

Exports