2007-04-25 16:42:59 -04:00
|
|
|
|
PEP: 3141
|
|
|
|
|
Title: A Type Hierarchy for Numbers (and other algebraic entities)
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Jeffrey Yasskin <jyasskin@gmail.com>
|
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 23-Apr-2007
|
|
|
|
|
Post-History: Not yet posted
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
2007-04-25 17:05:21 -04:00
|
|
|
|
This proposal defines a hierarchy of Abstract Base Classes (ABCs) (see
|
|
|
|
|
PEP 3119) to represent numbers and other algebraic entities similar to
|
|
|
|
|
numbers. It proposes:
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
* A hierarchy of algebraic concepts, including monoids, groups, rings,
|
|
|
|
|
and fields with successively more operators and constraints on their
|
|
|
|
|
operators. This will be added as a new library module named
|
|
|
|
|
"algebra".
|
|
|
|
|
|
|
|
|
|
* A hierarchy of specifically numeric types, which can be converted to
|
|
|
|
|
and from the native Python types. This will be added as a new
|
|
|
|
|
library module named "numbers".
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
Functions that take numbers as arguments should be able to determine
|
|
|
|
|
the properties of those numbers, and if and when overloading based on
|
|
|
|
|
types is added to the language, should be overloadable based on the
|
|
|
|
|
types of the arguments. This PEP defines some abstract base classes
|
|
|
|
|
that are useful in numerical calculations. A function can check that
|
|
|
|
|
variable is an instance of one of these classes and then rely on the
|
|
|
|
|
properties specified for them. Of course, the language cannot check
|
|
|
|
|
these properties, so where I say something is "guaranteed", I really
|
|
|
|
|
just mean that it's one of those properties a user should be able to
|
|
|
|
|
rely on.
|
|
|
|
|
|
|
|
|
|
This PEP tries to find a balance between providing fine-grained
|
|
|
|
|
distinctions and specifying types that few people will ever use.
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
Although this PEP uses terminology from PEP 3119, the hierarchy is
|
|
|
|
|
meaningful for any systematic method of defining sets of
|
|
|
|
|
classes. **Todo:** link to the Interfaces PEP when it's ready. I'm
|
2007-04-25 17:05:21 -04:00
|
|
|
|
also using the extra notation from PEP 3107 (annotations) to specify
|
2007-04-25 16:42:59 -04:00
|
|
|
|
some types.
|
|
|
|
|
|
|
|
|
|
Object oriented systems have a general problem in constraining
|
|
|
|
|
functions that take two arguments. To take addition as an example,
|
|
|
|
|
``int(3) + int(4)`` is defined, and ``vector(1,2,3) + vector(3,4,5)``
|
|
|
|
|
is defined, but ``int(3) + vector(3,4,5)`` doesn't make much sense. So
|
|
|
|
|
``a + b`` is not guaranteed to be defined for any two instances of
|
|
|
|
|
``AdditiveGroup``, but it is guaranteed to be defined when ``type(a)
|
|
|
|
|
== type(b)``. On the other hand, ``+`` does make sense for any sorts
|
|
|
|
|
of numbers, so the ``Complex`` ABC refines the properties for plus so
|
|
|
|
|
that ``a + b`` is defined whenever ``isinstance(a,Complex) and
|
|
|
|
|
isinstance(b,Complex)``, even if ``type(a) != type(b)``.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Monoids (http://en.wikipedia.org/wiki/Monoid) consist of a set with an
|
|
|
|
|
associative operation, and an identity element under that
|
|
|
|
|
operation. **Open issue**: Is a @classmethod the best way to define
|
|
|
|
|
constants that depend only on the type?::
|
|
|
|
|
|
|
|
|
|
class MonoidUnderPlus(Abstract):
|
|
|
|
|
"""+ is associative but not necessarily commutative and has an
|
|
|
|
|
identity given by plus_identity().
|
|
|
|
|
|
|
|
|
|
Subclasses follow the laws:
|
|
|
|
|
|
|
|
|
|
a + (b + c) === (a + b) + c
|
|
|
|
|
a.plus_identity() + a === a === a + a.plus_identity()
|
|
|
|
|
|
|
|
|
|
Sequences are monoids under plus (in Python) but are not
|
|
|
|
|
AdditiveGroups.
|
|
|
|
|
"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __add__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def plus_identity(cls):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
I skip ordinary non-commutative groups here because I don't have any
|
|
|
|
|
common examples of groups that use ``+`` as their operator but aren't
|
|
|
|
|
commutative. If we find some, the class can be added later.::
|
|
|
|
|
|
|
|
|
|
class AdditiveGroup(MonoidUnderPlus):
|
|
|
|
|
"""Defines a commutative group whose operator is +, and whose inverses
|
|
|
|
|
are produced by -x.
|
|
|
|
|
See http://en.wikipedia.org/wiki/Abelian_group.
|
|
|
|
|
|
|
|
|
|
Where a, b, and c are instances of the same subclass of
|
|
|
|
|
AdditiveGroup, the operations should follow these laws, where
|
|
|
|
|
'zero' is a.__class__.zero().
|
|
|
|
|
|
|
|
|
|
a + b === b + a
|
|
|
|
|
(a + b) + c === a + (b + c)
|
|
|
|
|
zero + a === a
|
|
|
|
|
a + (-a) === zero
|
|
|
|
|
a - b === a + -b
|
|
|
|
|
|
|
|
|
|
Some abstract subclasses, such as Complex, may extend the
|
|
|
|
|
definition of + to heterogenous subclasses, but AdditiveGroup only
|
|
|
|
|
guarantees it's defined on arguments of exactly the same types.
|
|
|
|
|
|
|
|
|
|
Vectors are AdditiveGroups but are not Rings.
|
|
|
|
|
"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __add__(self, other):
|
|
|
|
|
"""Associative commutative operation, whose inverse is negation."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-04-25 16:45:43 -04:00
|
|
|
|
**Open issue:** Do we want to give people a choice of which of the
|
|
|
|
|
following to define, or should we pick one arbitrarily?::
|
|
|
|
|
|
|
|
|
|
# AdditiveGroup, continued
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
def __neg__(self):
|
|
|
|
|
"""Must define this or __sub__()."""
|
|
|
|
|
return self.zero() - self
|
|
|
|
|
|
|
|
|
|
def __sub__(self, other):
|
|
|
|
|
"""Must define this or __neg__()."""
|
|
|
|
|
return self + -other
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def zero(cls):
|
|
|
|
|
"""A better name for +'s identity as we move into more mathematical
|
|
|
|
|
domains."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def plus_identity(cls):
|
|
|
|
|
return cls.zero()
|
|
|
|
|
|
|
|
|
|
Including Semiring (http://en.wikipedia.org/wiki/Semiring) would help
|
|
|
|
|
a little with defining a type for the natural numbers. That can be
|
|
|
|
|
split out once someone needs it (see ``IntegralDomain`` for how).::
|
|
|
|
|
|
|
|
|
|
class Ring(AdditiveGroup):
|
|
|
|
|
"""A mathematical ring over the operations + and *.
|
|
|
|
|
See http://en.wikipedia.org/wiki/Ring_%28mathematics%29.
|
|
|
|
|
|
|
|
|
|
In addition to the requirements of the AdditiveGroup superclass, a
|
|
|
|
|
Ring has an associative but not necessarily commutative
|
|
|
|
|
multiplication operation with identity (one) that distributes over
|
|
|
|
|
addition. A Ring can be constructed from any integer 'i' by adding
|
|
|
|
|
'one' to itself 'i' times. When R is a subclass of Ring, the
|
|
|
|
|
additive identity is R(0), and the multiplicative identity is
|
|
|
|
|
R(1).
|
|
|
|
|
|
|
|
|
|
Matrices are Rings but not Commutative Rings or Division
|
|
|
|
|
Rings. The quaternions are a Division Ring but not a
|
|
|
|
|
Field. The integers are a Commutative Ring but not a Field.
|
|
|
|
|
"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __init__(self, i:int):
|
|
|
|
|
"""An instance of a Ring may be constructed from an integer.
|
|
|
|
|
|
|
|
|
|
This may be a lossy conversion, as in the case of the integers
|
|
|
|
|
modulo N."""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __mul__(self, other):
|
|
|
|
|
"""Satisfies:
|
|
|
|
|
a * (b * c) === (a * b) * c
|
|
|
|
|
one * a === a
|
|
|
|
|
a * one === a
|
|
|
|
|
a * (b + c) === a * b + a * c
|
|
|
|
|
|
|
|
|
|
where one == a.__class__(1)
|
|
|
|
|
"""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def zero(cls):
|
|
|
|
|
return cls(0)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def one(cls):
|
|
|
|
|
return cls(1)
|
|
|
|
|
|
|
|
|
|
I'm skipping both CommutativeRing and DivisionRing here.::
|
|
|
|
|
|
|
|
|
|
class Field(Ring):
|
|
|
|
|
"""The class Field adds to Ring the requirement that * be a
|
|
|
|
|
commutative group operation except that zero does not have an
|
|
|
|
|
inverse.
|
|
|
|
|
See http://en.wikipedia.org/wiki/Field_%28mathematics%29.
|
|
|
|
|
|
|
|
|
|
Practically, that means we can define division on a Field. The
|
|
|
|
|
additional laws are:
|
|
|
|
|
|
|
|
|
|
a * b === b * a
|
|
|
|
|
a / a === a.__class_(1) # when a != a.__class__(0)
|
|
|
|
|
|
|
|
|
|
Division lets us construct a Field from any Python float,
|
|
|
|
|
although the conversion is likely to be lossy. Some Fields
|
|
|
|
|
include the real numbers, rationals, and integers mod a
|
|
|
|
|
prime. Python's ``float`` resembles a Field closely.
|
|
|
|
|
"""
|
|
|
|
|
def __init__(self, f:float):
|
|
|
|
|
"""A Field should be constructible from any rational number, which
|
|
|
|
|
includes Python floats."""
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __div__(self, divisor):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
Division is somewhat complicated in Python. You have both __floordiv__
|
|
|
|
|
and __div__, and ints produce floats when they're divided. For the
|
|
|
|
|
purposes of this hierarchy, ``__floordiv__(a, b)`` is defined by
|
|
|
|
|
``floor(__div__(a, b))``, and, since int is not a subclass of Field,
|
|
|
|
|
it's allowed to do whatever it wants with __div__.
|
|
|
|
|
|
|
|
|
|
There are four more reasonable classes that I'm skipping here in the
|
|
|
|
|
interest of keeping the initial library simple. They are:
|
|
|
|
|
|
|
|
|
|
``Algebraic``
|
|
|
|
|
Rational powers of its elements are defined (and maybe a few other
|
|
|
|
|
operations)
|
|
|
|
|
(http://en.wikipedia.org/wiki/Algebraic_number). Complex numbers
|
|
|
|
|
are the most well-known algebraic set. Real numbers are _not_
|
|
|
|
|
algebraic, but Python does define these operations on floats,
|
|
|
|
|
which makes defining this class somewhat difficult.
|
|
|
|
|
|
2007-04-26 18:33:22 -04:00
|
|
|
|
``Transcendental``
|
2007-04-25 16:42:59 -04:00
|
|
|
|
The elementary functions
|
|
|
|
|
(http://en.wikipedia.org/wiki/Elementary_function) are
|
|
|
|
|
defined. These are basically arbitrary powers, trig functions, and
|
|
|
|
|
logs, the contents of ``cmath``.
|
|
|
|
|
|
|
|
|
|
The following two classes can be reasonably combined with ``Integral``
|
|
|
|
|
for now.
|
|
|
|
|
|
|
|
|
|
``IntegralDomain``
|
|
|
|
|
Defines __divmod__.
|
|
|
|
|
(http://darcs.haskell.org/numericprelude/docs/html/Algebra-IntegralDomain.html#t%3AC)
|
|
|
|
|
|
|
|
|
|
``PrincipalIdealDomain``
|
|
|
|
|
Defines gcd and lcm.
|
|
|
|
|
(http://darcs.haskell.org/numericprelude/docs/html/Algebra-PrincipalIdealDomain.html#t%3AC)
|
|
|
|
|
|
|
|
|
|
If someone needs to split them later, they can use code like::
|
|
|
|
|
import numbers
|
|
|
|
|
class IntegralDomain(Ring): ...
|
|
|
|
|
numbers.Integral.__bases__ = (IntegralDomain,) + numbers.Integral.__bases__
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Finally, we get to numbers. This is where we switch from the "algebra"
|
|
|
|
|
module to the "numbers" module.::
|
|
|
|
|
|
|
|
|
|
class Complex(Ring, Hashable):
|
|
|
|
|
"""The ``Complex`` ABC indicates that the value lies somewhere
|
|
|
|
|
on the complex plane, not that it in fact has a complex
|
|
|
|
|
component: ``int`` is a subclass of ``Complex``. Because these
|
|
|
|
|
actually represent complex numbers, they can be converted to
|
|
|
|
|
the ``complex`` type.
|
|
|
|
|
|
|
|
|
|
``Complex`` finally gets around to requiring its subtypes to
|
|
|
|
|
be immutable so they can be hashed in a standard way.
|
|
|
|
|
|
|
|
|
|
``Complex`` also requires its operations to accept
|
|
|
|
|
heterogenous arguments. Subclasses should override the
|
|
|
|
|
operators to be more accurate when they can, but should fall
|
|
|
|
|
back on the default definitions to handle arguments of
|
|
|
|
|
different (Complex) types.
|
|
|
|
|
|
|
|
|
|
**Open issue:** __abs__ doesn't fit here because it doesn't
|
|
|
|
|
exist for the Gaussian integers
|
|
|
|
|
(http://en.wikipedia.org/wiki/Gaussian_integer). In fact, it
|
|
|
|
|
only exists for algebraic complex numbers and real numbers. We
|
|
|
|
|
could define it in both places, or leave it out of the
|
|
|
|
|
``Complex`` classes entirely and let it be a custom extention
|
|
|
|
|
of the ``complex`` type.
|
|
|
|
|
|
|
|
|
|
The Gaussian integers are ``Complex`` but not a ``Field``.
|
|
|
|
|
"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __complex__(self):
|
|
|
|
|
"""Any Complex can be converted to a native complex object."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
|
return hash(complex(self))
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def real(self) => Real:
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def imag(self) => Real:
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __add__(self, other):
|
|
|
|
|
"""The other Ring operations should be implemented similarly."""
|
|
|
|
|
if isinstance(other, Complex):
|
|
|
|
|
return complex(self) + complex(other)
|
|
|
|
|
else:
|
|
|
|
|
return NotImplemented
|
|
|
|
|
|
|
|
|
|
``FractionalComplex(Complex, Field)`` might fit here, except that it
|
|
|
|
|
wouldn't give us any new operations.::
|
|
|
|
|
|
|
|
|
|
class Real(Complex, TotallyOrdered):
|
|
|
|
|
"""Numbers along the real line. Some subclasses of this class
|
|
|
|
|
may contain NaNs that are not ordered with the rest of the
|
|
|
|
|
instances of that type. Oh well. **Open issue:** what problems
|
|
|
|
|
will that cause? Is it worth it in order to get a
|
|
|
|
|
straightforward type hierarchy?
|
|
|
|
|
"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __float__(self):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
def __complex__(self):
|
|
|
|
|
return complex(float(self))
|
|
|
|
|
def real(self) => self.__class__:
|
|
|
|
|
return self
|
|
|
|
|
def imag(self) => self.__class__:
|
|
|
|
|
return self.__class__(0)
|
|
|
|
|
def __abs__(self) => self.__class__:
|
|
|
|
|
if self < 0: return -self
|
|
|
|
|
else: return self
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FractionalReal(Real, Field):
|
|
|
|
|
"""Rationals and floats. This class provides concrete
|
|
|
|
|
definitions of the other four methods from properfraction and
|
|
|
|
|
allows you to convert fractional reals to integers in a
|
|
|
|
|
disciplined way.
|
|
|
|
|
"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def properfraction(self) => (int, self.__class__):
|
|
|
|
|
"""Returns a pair (n,f) such that self == n+f, and:
|
|
|
|
|
* n is an integral number with the same sign as self; and
|
|
|
|
|
* f is a fraction with the same type and sign as self, and with
|
|
|
|
|
absolute value less than 1.
|
|
|
|
|
"""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
def floor(self) => int:
|
|
|
|
|
n, r = self.properfraction()
|
|
|
|
|
if r < 0 then n - 1 else n
|
|
|
|
|
def ceiling(self) => int: ...
|
|
|
|
|
def __trunc__(self) => int: ...
|
|
|
|
|
def round(self) => int: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Open issue:** What's the best name for this class? RealIntegral? Integer?::
|
|
|
|
|
|
|
|
|
|
class Integral(Real):
|
|
|
|
|
"""Integers!"""
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __int__(self):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
def __float__(self):
|
|
|
|
|
return float(int(self))
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __or__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __xor__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __and__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __lshift__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __rshift__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __invert__(self):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Floating point values may not exactly obey several of the properties
|
|
|
|
|
you would expect from their superclasses. For example, it is possible
|
|
|
|
|
for ``(large_val + -large_val) + 3 == 3``, but ``large_val +
|
|
|
|
|
(-large_val + 3) == 0``. On the values most functions deal with this
|
|
|
|
|
isn't a problem, but it is something to be aware of. Types like this
|
|
|
|
|
inherit from ``FloatingReal`` so that functions that care can know to
|
|
|
|
|
use a numerically stable algorithm on them. **Open issue:** Is this
|
|
|
|
|
the proper way to handle floating types?::
|
|
|
|
|
|
|
|
|
|
class FloatingReal:
|
|
|
|
|
"""A "floating" number is one that is represented as
|
|
|
|
|
``mantissa * radix**exponent`` where mantissa, radix, and
|
|
|
|
|
exponent are all integers. Subclasses of FloatingReal don't
|
|
|
|
|
follow all the rules you'd expect numbers to follow. If you
|
|
|
|
|
really care about the answer, you have to use numerically
|
|
|
|
|
stable algorithms, whatever those are.
|
|
|
|
|
|
|
|
|
|
**Open issue:** What other operations would be useful here?
|
|
|
|
|
|
|
|
|
|
These include floats and Decimals.
|
|
|
|
|
"""
|
|
|
|
|
@classmethod
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def radix(cls) => int:
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def digits(cls) => int:
|
|
|
|
|
"""The number of significant digits of base cls.radix()."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def exponentRange(cls) => (int, int):
|
|
|
|
|
"""A pair of the (lowest,highest) values possible in the exponent."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def decode(self) => (int, int):
|
|
|
|
|
"""Returns a pair (mantissa, exponent) such that
|
|
|
|
|
mantissa*self.radix()**exponent == self."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Inspiration
|
|
|
|
|
===========
|
|
|
|
|
http://hackage.haskell.org/trac/haskell-prime/wiki/StandardClasses
|
|
|
|
|
http://repetae.net/john/recent/out/classalias.html
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
2007-04-25 17:05:21 -04:00
|
|
|
|
.. [1] Introducing Abstract Base Classes
|
2007-04-25 16:42:59 -04:00
|
|
|
|
(http://www.python.org/dev/peps/pep-3119/)
|
|
|
|
|
|
2007-04-25 17:05:21 -04:00
|
|
|
|
.. [2] Function Annotations
|
2007-04-25 16:42:59 -04:00
|
|
|
|
(http://www.python.org/dev/peps/pep-3107/)
|
|
|
|
|
|
|
|
|
|
.. [3] Possible Python 3K Class Tree?, wiki page created by Bill Janssen
|
|
|
|
|
(http://wiki.python.org/moin/AbstractBaseClasses)
|
|
|
|
|
|
2007-04-25 17:05:21 -04:00
|
|
|
|
.. [4] NumericPrelude: An experimental alternative
|
2007-04-25 16:42:59 -04:00
|
|
|
|
hierarchy of numeric type classes
|
|
|
|
|
(http://darcs.haskell.org/numericprelude/docs/html/index.html)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Acknowledgements
|
|
|
|
|
----------------
|
|
|
|
|
|
2007-05-01 21:19:58 -04:00
|
|
|
|
Thanks to Neal Norwitz for helping me through the PEP process.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-04-25 17:05:21 -04:00
|
|
|
|
The Haskell Numeric Prelude [4]_ nicely condensed a lot
|
2007-04-25 16:42:59 -04:00
|
|
|
|
of experience with the Haskell numeric hierarchy into a form that was
|
|
|
|
|
relatively easily adaptable to Python.
|
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|