The first section introduces generic versions of Clojure's [[zero?]], [[+]], [[-]], [[*]] and [[/]] operations. Any type that can implement all of of these operations forms a mathematical Field.
There are, of course, other technical names for types that can only implement a subset of these operations, and more specializations of those names depending on whether or not the implementation of these binary operations is commutative or associative. (See Semigroup and Monoid first, and start exploring the realm of abstract algebra from there.)
This library takes a permissive stance on extensibility. Types extend the arithmetic operators by extending their unary or binary cases:
And the higher arity version reduces its list of arguments using this binary operation. This makes it possible and easy to make the arithmetic operators combine different types! It's up to you to do this in a mathematically responsible way.
Dispatch occurs via [[value/argument-kind]]. Documentation on how to extend each generic operation to some new type is sparse. Have a look at [[emmy.complex]] for an example of how to do this.
In the binary arity of [[*]] we test for exact (numerical) zero because it is possible to produce a wrong-type zero here, as follows:
We are less worried about the zero? below, because any invertible matrix is square.
This next batch of generics covers various forms of exponentation and its inverse.
[[expt]] can be defined (as a default) in terms of repeated multiplication, if the exponent is a (native) integer. The native requirement is simply due to the [[default-expt]] implementation above, which uses functions like quot
and even?
that are not generic.
For all other arguments, default [[expt]] requires that [[exp]], [[mul]] and [[log]] be defined already on the type.
Thanks to John D Cook's post 'Bootstrapping a minimal math library' for his inspiration on the defaults and order of functions in the following sections.
Given [[sin]] and [[cos]] implementations, it's possible to define the rest of the trigonometric functions with proper defaults.
Given an [[atan]] implementation, types automatically gain default methods for the rest of the inverse trig functions.
This section defines [[sinc]] and [[tanc]], and the hyperbolic variants [[sinhc]] and [[tanhc]].
NOTE that we don't define
cosc
. This StackExchange post has a nice explanation of why the analogouscosc
doesn't belong: "The motivation for functions such as Loading..., Loading..., Loading..., Loading... is to consider the behaviour of a ratio with limit 1 as Loading.... There is no such motivation for Loading..., since Loading...."
The Julia language does define a cosc
, but strangely makes it equal to the derivative of sinc
, by analogy with cos
being the derivative of sin
. This felt to me to be a step too far. Here's the Julia manual page for cosc
.
Wikipedia does have a page on coshc
, defined as Loading....
Scalars include complex numbers, but for the purposes of dot/inner-products these are interpreted as pairs of real numbers, where conjugate is identity. So this seems to be a sane default.
defgeneric
installs a metadata-lookup handler for keywords by default; in this case, we want to override that feature so that keywords simplify to themselves.
This call registers a symbol for any non-multimethod we care about. These will be returned instead of the actual function body when the user calls (g/freeze fn)
, for example.