Numbers is a tiny Elixir package that facilitates the creation of libraries that want to be able to use any kind of Numberlike type.
Some known custom numeric types that implement Numbers' protocols:
- Ratio -- rational numbers.
- Decimal -- arbitrary precision decimal numbers.
- Tensor -- Vectors, Matrices and higher-order tensors.
- ComplexNum -- Complex numbers.
Just add one (or multiple) of these libraries to your project, together with Numbers, and you're good to go!
Starting at version 5, Numbers contains a set of protocols that can be independently implemented for your data structures.
Each protocol maps to a single arithmetical operation that your data structure might support.
Because protocols are used, Numbers can dispatch quite fast! Also, Numbers does not restrict your modules to any special naming schemes (as was the case with older versions of Numbers that used a Behaviour).
The following operations are supported:
add
for addition, by implementingNumbers.Protocols.Addition
.sub
for subtraction, by implementingNumbers.Protocols.Subtraction
.mult
for multiplication, by implementingNumbers.Protocols.Multiplication
.div
for division, by implementingNumbers.Protocols.Division
.minus
for unary minus (negation), by implementingNumbers.Protocols.Minus
.abs
to calculate the absolute value of a number, by implementingNumbers.Protocols.Absolute
.pow
for calculating integer powers, by implementingNumbers.Protocols.Exponentiation
. A special helper inNumbers.Helpers.pow_by_sq
can be used inside this implementation to automatically make use of the 'Exponentiation by Squaring' algorithm.to_float
for (possibly lossy) conversion to the built-in Float datatype, by implementingNumbers.Protocols.ToFloat
.
Numbers does not automatically transform numbers from one type to another if one of the functions is called with two different types.
Frequently you do want to use other data types together with your custom data type. For this, a custom coercion can be specified,
using Coerce.defcoercion
as exposed by the Coerce
library that Numbers
depends on.
The only coercion that ships with Numbers itself, is a coercion of Integers to Floats, meaning that they work the same way as when using the standard library math functions with these types.
You can opt-in to overloaded +, -, *, /
operators by calling use Numbers, overload_operators: true
.
This allows you to use these inline operators for all other Numberlike types.
The library uses a conditional compilation technique to make sure that you will still be able to use the operators inside guards for built-in integers and floats.
As example consider:
defmodule An.Example do
use Numbers, overload_operators: true
def foo(a, b) when a + b < 10 do # Uses the normal guard-safe '+' operator (e.g. Kernel.+/2)
42
end
def foo(c, d) do
c + d # Uses the overloaded '+' operator.
end
end
Using built-in numbers:
iex> alias Numbers, as: N
iex> N.add(1, 2)
3
iex> N.mult(3,5)
15
iex> N.mult(1.5, 100)
150.0
Using Decimals: (requires the Decimal library.)
iex> alias Numbers, as: N
iex> d = Decimal.new(2)
iex> N.div(d, 10)
#Decimal<0.2>
iex> small_number = N.div(d, 1234)
#Decimal<0.001620745542949756888168557536>
iex> N.pow(small_number, 100)
The package can be installed as:
- Add
numbers
to your list of dependencies inmix.exs
:
def deps do
[{:numbers, "~> 5.2"}]
end
- 5.2.4 Updates
:decimal
dependency to now allow both version1.x
as well as version2.x
. - 5.2.3 Updates
:decimal
dependency to1.9
or newer, and replaces deprecatedDecimal.minus/1
call withDecimal.negate/1
- 5.2.2 Updated
mix.exs
to useextra_applications
rather than manually overriddingapplications
. This drops support for now very old versions of Elixir (< v1.4) but ensures proper support with Elixir v1.11 and beyond. - 5.2.1 Better error message when wrongly using an operator at the left side of a match (e.g.
a - 1 = 10
). - 5.2.0 Ensures that overloaded operators do not prevent built-in operators to be used in guards.
- 5.1.1 Makes
Decimal
dependency version less specific to play nicer with other libraries :-). - 5.1.0 Possibility to import overloaded operator variants. Also, greatly improved documentation.
- 5.0.0 MAJOR OVERHAUL: New implementation based on a set of Protocols. Should be a lot faster and easier on implementers. Also uses a new method to perform coercions based on the
Coerce
library. Announcement post - 4.0.0 Breaking change: Move
Numeric
toNumbers.Numeric
, to follow proper code organization conventions. - 3.0.1 Improved README
- 3.0.0 Remove public
Numbers.coerce/2
function, as it had confused naming and very limited use. Added optionalNumeric.coerce/2
callback (which works very different from the oldNumbers.coerce/2
function) which is now used underwater when coercion should happen. - 2.0.3 Improving documentation.
- 2.0.2 Adding many tests.
- 2.0.1 Fixing error message that is shown when conversion to float is not possible to use the Inspect protocol.
- 2.0.0 Breaking change,
mul
->mult
. - 1.0.0 First Stable Version.