The numbers supported by the Scheme programming language form a tower, where the set of numbers at each level is a subset of the set at the level above. The levels are, respectively: integers, rationals, reals,1 and complex numbers. This article investigates the potential extension of Scheme's numeric tower upward to include quaternions [3], a set of hypercomplex numbers that properly includes the complex numbers, and by extension, all the numbers below.
A quaternion q is a 4-tuple (w,x,y,z), where w, x, y, and z are real. To give it the aspect of a number, it is written w + ix + jy + kz, where i, j, and k are imaginary units that satisfy the conditions ii = jj = kk = - 1, ij = - ji = k, jk = - kj = i, and ki = - ik = j.2 Clearly, multiplication on quaternions does not commute. w is q's real (or scalar) part, and x, y, and z are its i-, j-, and k-part respectively. ix + jy + kz is q's vector part. A quaternion with zero j- and k-parts is an ordinary complex number, and if, further, its i-part is also zero, it is just a real number. A quaternion with zero real part is called a vector quaternion or, simply, vector.3
Notation: We will use qn (where n is either an
integer or absent) interchangeably with
wn + ixn + jyn + kzn. qn's vector part
ixn + jyn + kzn is also written vn and its unit
vector (see below) is written un. E =>
e means that the Scheme expression E
evaluates to a Scheme value e. In order to prevent
confusion, we add a subscript q to the name of a
Scheme procedure that has been made quaternion-aware.
For example, exp is the exponential procedure
described in R5RS [4]; whereas expq
is the same procedure that has been extended to accept
a quaternion argument.
Recall that Scheme complex numbers that are not also
real are represented in rectangular (or cartesian) notation as
a+bi or
a-bi, where a is optional (for
numbers that are purely imaginary) or real, and b is
a non-negative real. Examples:
+i -i +2i -2i 3+4i
(The sign is never
optional, eg, i must be represented as +i rather
than i, which latter is parsed as an identifier.)
We can naturally extend this to
quaternions: thus q above is written
w+xi+yj+zk.
The pluses can be
minuses, and any part that is zero may be omitted, with
the proviso that a vector quaternion must always start
with a sign or an explicit zero real part, in order to prevent being mistaken for an
identifier. Examples of some Scheme quaternions that
are not also complex numbers:
+j +3k -2+3i-3k -6i-6k 3-4i+5j+6k
Scheme also permits users to enter complex numbers in a
notation that uses polar coordinates, viz,
µ@
, where µ and
are real.
A corresponding notation for quaternions
is also possible. For example,
q = w + ix + jy + kz has the polar (or spherical or
curvilinear) coordinates
µ,
,
,
, which are related to the
rectangular coordinates as follows:4

µ,
,
,
are respectively the
magnitude, angle, colatitude, and longitude
of q.5
Extending Scheme's notation for complex numbers,
q may be represented as
µ @
%
&
. Any zero angle may
be omitted along with its prefix. If the
colatitude and longitude are both zero, the number is
complex.
As for complex numbers, polar notation is used only to enter quaternions. Scheme outputs quaternion values exclusively in rectangular. Thus,
-3@4%-5&6 => 1.960930862590836+0.6440287493492101i+2.0904336369493484-0.6083291310311992k
The predicate quaternion? checks if its argument is
a quaternion. Because quaternions now form the pinnacle of Scheme's
numeric tower, quaternion? returns true (#t) for any
number, and thus agrees with Scheme's number? predicate.
The Scheme complex-number procedures real-part,
imag-part, magnitude, and angle are
extended to accept quaternion arguments. They return,
respectively, the real part, the i-part, the magnitude,
and the angle, of their argument. To these are
added four new related procedures: jmag-part,
kmag-part, colatitude, and longitude
return, respectively, the j-part, k-part, colatitude, and
longitude of their argument.
The procedure vector-part returns the vector part of its
argument.
The procedures make-rectangular and
make-polar are extended to take two extra arguments
in order to produce quaternions that aren't just complex numbers.
(make-rectangularq 3 4 5 6) => 3+4i+5j+6k (make-polarq -3 4 -5 6) => 1.960930862590836+0.6440287493492101i+2.0904336369493484-0.6083291310311992k
Note that the result printed by Scheme is the rectangular equivalent of the polar coordinates specified.
Every quaternion q has a special vector quaternion u called its unit vector, which is defined as

In Scheme,
(define unit-vector (lambda (q) (*q (vector-part q) (/ (magnitudeq q)))))
The magnitude of a unit vector is of course unity.
Scheme numerical procedures that work on complex numbers can be naturally extended to accept quaternion arguments.6
The procedures =, +, - extend in the expected manner for
quaternion arguments --
they all operate on the parts severally.
(=q 3+4j 3+4j) => #t (=q 3+4j 3+4j 3+4k) => #f (+q 3+4j 3+4j) => 6+8j (+q 3+4j 3+4j 3+4k) => 9+8j+4k (-q 4+5j 2-3k) => 2+8k (-q 2+3j-4k) => -2-3j+4k
The multiplication procedure *q (using the identities
ii = jj = kk = ijk = - 1) can be implemented as
follows:

Example:
(*q 4-3i+2j+6k 4+5i-7j+9k) => -9+68i+37j+71k
Generally, multiplication is not commutative. However if two quaternions have the same unit vector disregarding sign, they do commute on multiplication, and furthermore, their product has the same unit vector. The square of a unit vector (its product with itself) is - 1.
If q1, q2 are vector quaternions, ie,
w1 = w2 = 0, then the real part of
q1 q2 is the negative of the dot product q1 · q2
and the vector part of q1 q2 is the cross product q1
q2.
(define dot-product (lambda (q1 q2) (if (not (and (= (real-partq q1) 0) (= (real-partq q2) 0))) (error 'dot-product "arguments must be vector quaternions") (- (real-partq (*q q1 q2)))))) (define cross-product (lambda (q1 q2) (if (not (and (= (real-partq q1) 0) (= (real-partq q2) 0))) (error 'cross-product "arguments must be vector quaternions") (vector-part (*q q1 q2)))))
If the imaginary parts of q1 and q2 are severally the negatives of each other, then the product q1 q2 is real. Such q1, q2 are termed conjugates of each other. They have the same magnitude, and their product is the square of that magnitude.
(define conjugate (lambda (q) (make-rectangularq (real-partq q) (- (imag-partq q)) (- (jmag-part q)) (- (kmag-part q)))))
We can use fact (1) to implement the reciprocal of a
quaternion. Multiply both numerator and
denominator of 1/q by q's conjugate. Unary /
is the Scheme procedure for reciprocals. Example:
(/q 8+3i+4j+5k) => 0.07017543859649122-0.02631578947368421i-0.03508771929824561j-0.043859649122807015k
Multiplication by the reciprocal leads to a definition of division. Dividing q1 by q2 is viewed as multiplying q1 by q2-1. The noncommutativity of multiplication allows a choice between putting the reciprocal before or after q1. Thus division from the left and from the right are different.
In keeping with the visual layout of Scheme's /
procedure, we can define
(/q q1 q2 ...) with
(*q q1 (/q q2) ...), but to prevent confusion,
it is best to always use
explicit reciprocals (unary /q) and multiplication
(*q).
As for complex numbers, we use the Maclaurin series to define quaternion extensions of the transcendental functions. Thus, for quaternion q = w + uv, where uq is its unit vector and w and v are real, we can derive

u functions much the same as i for complex numbers. The difference from the complex case is that u depends on (ie, varies with) q, whereas i is the same for all complex z.
In Scheme,
(define expq (lambda (q) (let ((w (real-partq q)) (u (unit-vector q)) (v (magnitudeq (vector-part q)))) (*q (exp w) (+q (cos v) (*q u (sn v)))))))
Note that the unit vectors of q and eq are the same, disregarding sign. The inverse of of the exponential function is the natural logarithm,7 and is given by:

(where the log on the right only need be defined
for positive reals). To make the log function single-valued,
we choose tan-1 (v/w) between -
adn
. In Scheme,
(define logq (lambda (q) (let ((w (real-partq q)) (u (unit-vector q)) (v (magnitudeq (vector-part q)))) (+q (*q 1/2 (log (magnitudeq q))) (*q u (atan v w))))))
We can use logq to define exptq, since:

In Scheme,
(define exptq (lambda (q1 q2) (expq (*q (logq q1) q2))))
The sqrtq function then is:
(define sqrtq (lambda (q) (exptq q 1/2)))
For sin q and cos q, we have (using the Maclaurin's series for sinh and cosh):

In Scheme,8
(define sinq (lambda (q) (let ((w (real-partq q)) (u (unit-vector q)) (v (magnitudeq (vector-part q)))) (+q (*q (sin w) (cosh v)) (*q u (cos w) (sinh v)))))) (define cosq (lambda (q) (let ((w (real-partq q)) (u (unit-vector q)) (v (magnitudeq (vector-part q)))) (-q (*q (cos w) (cosh v)) (*q u (sin w) (sinh v))))))
tan q can be defined as (sin q)/(cos q). Since sin q, and cos q have the same unit vector as q, the direction of the division doesn't matter.
The following are derived using Maclaurin's and straightforward quadratic solving:

In Scheme,
(define asinq (lambda (q) (let ((u (unit-vector q))) (-q (*q u (logq (+q (*q u q) (sqrtq (-q 1 (*q q q)))))))))) (define acosq (lambda (q) (let ((u (unit-vector q))) (-q (*q u (logq (+q q (sqrtq (-q (*q q q) 1))))))))) (define atanq (lambda (q) (let ((u (unit-vector q))) (*q 1/2 u (logq (*q (+q u q) (/q (-q u q))))))))
The following collects the signatures of all the quaternion procedures proposed above. The types number and quaternion are synonymous. I shall use the name quaternion merely to emphasize that a function result can be a quaternion. Arguments enclosed in brackets are optional.
quaternion? : any
boolean
real-partq : number
real
imag-partq : number
real
jmag-part : number
real
kmag-part : number
real
vector-part : number
quaternion
unit-vector : number
quaternion
magnitudeq : number
real
angleq : number
real
colatitude : number
real
longitude : number
real
make-rectangularq : real
real [
real
real]
quaternion
make-polarq : real
real [
real
real]
quaternion
+q : number*
quaternion
-q : number+
quaternion
*q : number*
quaternion
/q : number+
quaternion
dot-product : number
number
real
cross-product : number
number
quaternion
conjugate : number
quaternion
expq : number
quaternion
logq : number
quaternion
exptq : number
number
quaternion
sqrtq : number
quaternion
sinq : number
quaternion
cosq : number
quaternion
tanq : number
quaternion
asinq : number
quaternion
acosq : number
quaternion
atanq : number
quaternion
atan : real
real
real (two arguments)
| [1] |
A D Aleksandrov, A N Kolmogorov, and M A Lavrentev, Mathematics: Its Content, Methods and Meaning, Dover Publications, 1963.
|
| [2] |
Ruel V Churchill, Complex Variables and Applications, McGraw-Hill, 2nd ed, 1960.
|
| [3] |
William Rowan Hamilton, On Quaternions; or on a new System of Imaginaries in Algebra, Philosophical Magazine, 1844-1850.
|
| [4] |
Richard Kelsey, William Clinger, and Jonathan Rees (eds), Revised^5 Report on the Algorithmic Language Scheme (``R5RS''), 1998.
|
| [5] |
Cornelius Lanczos, The Variational Principles of Mechanics, Dover Publications, 4th ed, 1986.
|
| [6] |
Gerald Jay Sussman and Jack Wisdom with Meinhard E Meyer, Structure and Intepretation of Classical Mechanics, MIT Press, 2001.
|
| [7] |
Peter Guthrie Tait, Quaternions, Encyclopedia Britannica, 9th ed, 1886, vol XX, pp 160-164.
|
1 In practice, an implementation does not differentiate between rationals and reals.
2 Invoking the associativity of multiplication, these conditions can be more succinctly stated as ii = jj = kk = ijk = - 1.
3 These are
indeed the vectors familiarly used in physics to
represent 3-dimensional quantities. They are not be confused
with Scheme's one-dimensional array data type called vector.
4 The rectangular coordinates can be deduced from the polar as follows:

5 The more usual terms for magnitude and angle are modulus and amplitude respectively, but we wish to remain compatible with Scheme's already established names for complex numbers.
6 Procedures like <, >, <=,
>=, positive?, negative?, min,
max, floor, ceiling, truncate,
round, rationalize two-argument atan,
make-rectangular and make-polar are not defined
for quaternions, but they are not defined for complex
numbers either.
7 Following Scheme practice, we use log to mean loge rather than log10.
8 sinh and cosh for reals are not
part of the Scheme standard [4] but they
can be readily defined using the
identities
