Today I started writing a small library to perform computations with Complex Numbers. (ComplexNum, it is still very much unfinished). While working on that, I realized that I did not only wanted to be able to specify the real and imaginary parts as
Floats, but also
Rationals or other numeric data types.
This made me realize that it was possible to create a more general ‘dispatching’ module, as long as each of the numeric data types followed the same API (i.e. the same Behaviour):
Numbers was born.
What does it do?
It allows you to call
Numbers.mult, etc. regardless of what data types you are performing arithmetic with. As long as they are the same type (or one of them is a built-in data type such as an integer or float, in which case they will be attempted to automatically be converted to the custom data type), it will Just Work™.
iex> alias Numbers, as: N iex> N.add(1, 2) 3 iex> N.mul(3,5) 15 iex> N.mul(1.5, 100) 150.0
Using Decimals: (requires the
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)
Using Rationals: (requires the
iex> use Ratio, operators: false iex> res = N.add(1 <|> 2, 3 <|> 5) 11 <|> 10 iex> N.mult(res, 10)
How does it work?
Simply put, the
Numbers module accepts for most functions two arguments that should be of the same struct type (optionally, one of them could be an integer or float which is automatically converted by Numbers to the struct of the other argument). It will extract the module name of the struct, and call the functions named
sub, etc. on that module.
Numeric is a Behaviour, to follow.
Numbers dispatches that behaviour, which means that you can build functions and data types that wrap any kind of number, including custom-built ones.
What data types are supported right now?
Right now, I know of the following data types that follow the behaviour:
- built-in Integers
- built-in Floats
- Ratio for rational numbers.
Decimal for decimal numbers. (Does not yet formally implement the Numeric behaviour using
@behaviour, I’ll create a Pull Request on its repository adding just that shortly. In the meantime, as it already informally follows the behaviour, it already simply works!).
One of the things I like the most, is that the following structures both dispatch using Numbers internally as well as implement the Numeric Behaviour, meaning that they can be used as composite Numeric types, when the type they contain follow the Numeric Behaviour. (So you can do elementwise addition of multiple Vectors of Decimals, or even a Matrix filled with Vectors of Complex numbers of Floats). The possibilities are endless!
- ComplexNum for complex numbers. (as long as the data type used for the real/imaginary parts itself follows Numeric.
- Tensor for Vectors, Matrices and higher-order Tensors.
Tl;Dr: Numbers is a wrapper exposing the generic functionality all kinds of (custom or built-in) numeric datatypes have. So you can use it to make your code number-agnostic!
I look forward to any and all feedback from you!