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 4tuple (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 kpart respectively. ix + jy + kz is q's vector part. A quaternion with zero j and kparts is an ordinary complex number, and if, further, its ipart 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 q_{n} (where n is either an
integer or absent) interchangeably with
w_{n} + ix_{n} + jy_{n} + kz_{n}. q_{n}'s vector part
ix_{n} + jy_{n} + kz_{n} is also written v_{n} and its unit
vector (see below) is written u_{n}. 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 quaternionaware.
For example, exp
is the exponential procedure
described in R5RS [4]; whereas exp_{q}
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
abi
, where a is optional (for
numbers that are purely imaginary) or real, and b is
a nonnegative 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+3i3k 6i6k 34i+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.09043363694934840.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 complexnumber procedures realpart
,
imagpart
, magnitude
, and angle
are
extended to accept quaternion arguments. They return,
respectively, the real part, the ipart, the magnitude,
and the angle, of their argument. To these are
added four new related procedures: jmagpart
,
kmagpart
, colatitude
, and longitude
return, respectively, the jpart, kpart, colatitude, and
longitude of their argument.
The procedure vectorpart
returns the vector part of its
argument.
The procedures makerectangular
and
makepolar
are extended to take two extra arguments
in order to produce quaternions that aren't just complex numbers.
(makerectangular_{q} 3 4 5 6) => 3+4i+5j+6k (makepolar_{q} 3 4 5 6) => 1.960930862590836+0.6440287493492101i+2.09043363694934840.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 unitvector (lambda (q) (*_{q} (vectorpart q) (/ (magnitude_{q} 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 23k) => 2+8k (_{q} 2+3j4k) => 23j+4k
The multiplication procedure *_{q}
(using the identities
ii = jj = kk = ijk =  1) can be implemented as
follows:
Example:
(*_{q} 43i+2j+6k 4+5i7j+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 q_{1}, q_{2} are vector quaternions, ie, w_{1} = w_{2} = 0, then the real part of q_{1} q_{2} is the negative of the dot product q_{1} · q_{2} and the vector part of q_{1} q_{2} is the cross product q_{1} q_{2}.
(define dotproduct (lambda (q1 q2) (if (not (and (= (realpart_{q} q1) 0) (= (realpart_{q} q2) 0))) (error 'dotproduct "arguments must be vector quaternions") ( (realpart_{q} (*_{q} q1 q2)))))) (define crossproduct (lambda (q1 q2) (if (not (and (= (realpart_{q} q1) 0) (= (realpart_{q} q2) 0))) (error 'crossproduct "arguments must be vector quaternions") (vectorpart (*_{q} q1 q2)))))
If the imaginary parts of q_{1} and q_{2} are severally the negatives of each other, then the product q_{1} q_{2} is real. Such q_{1}, q_{2} are termed conjugates of each other. They have the same magnitude, and their product is the square of that magnitude.
(define conjugate (lambda (q) (makerectangular_{q} (realpart_{q} q) ( (imagpart_{q} q)) ( (jmagpart q)) ( (kmagpart 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.070175438596491220.02631578947368421i0.03508771929824561j0.043859649122807015k
Multiplication by the reciprocal leads to a definition of division. Dividing q_{1} by q_{2} is viewed as multiplying q_{1} by q_{2}^{1}. The noncommutativity of multiplication allows a choice between putting the reciprocal before or after q_{1}. 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 exp_{q} (lambda (q) (let ((w (realpart_{q} q)) (u (unitvector q)) (v (magnitude_{q} (vectorpart q)))) (*_{q} (exp w) (+_{q} (cos v) (*_{q} u (sn v)))))))
Note that the unit vectors of q and e^{q} 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 singlevalued, we choose tan^{1} (v/w) between  adn . In Scheme,
(define log_{q} (lambda (q) (let ((w (realpart_{q} q)) (u (unitvector q)) (v (magnitude_{q} (vectorpart q)))) (+_{q} (*_{q} 1/2 (log (magnitude_{q} q))) (*_{q} u (atan v w))))))
We can use log_{q}
to define expt_{q}
, since:
In Scheme,
(define expt_{q} (lambda (q1 q2) (exp_{q} (*_{q} (log_{q} q1) q2))))
The sqrt_{q}
function then is:
(define sqrt_{q} (lambda (q) (expt_{q} q 1/2)))
For sin q and cos q, we have (using the Maclaurin's series for sinh and cosh):
In Scheme,^{8}
(define sin_{q} (lambda (q) (let ((w (realpart_{q} q)) (u (unitvector q)) (v (magnitude_{q} (vectorpart q)))) (+_{q} (*_{q} (sin w) (cosh v)) (*_{q} u (cos w) (sinh v)))))) (define cos_{q} (lambda (q) (let ((w (realpart_{q} q)) (u (unitvector q)) (v (magnitude_{q} (vectorpart 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 asin_{q} (lambda (q) (let ((u (unitvector q))) (_{q} (*_{q} u (log_{q} (+_{q} (*_{q} u q) (sqrt_{q} (_{q} 1 (*_{q} q q)))))))))) (define acos_{q} (lambda (q) (let ((u (unitvector q))) (_{q} (*_{q} u (log_{q} (+_{q} q (sqrt_{q} (_{q} (*_{q} q q) 1))))))))) (define atan_{q} (lambda (q) (let ((u (unitvector q))) (*_{q} 1/2 u (log_{q} (*_{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
realpart_{q}
: number real
imagpart_{q}
: number real
jmagpart
: number real
kmagpart
: number real
vectorpart
: number quaternion
unitvector
: number quaternion
magnitude_{q}
: number real
angle_{q}
: number real
colatitude
: number real
longitude
: number real
makerectangular_{q}
: real real [ real real] quaternion
makepolar_{q}
: real real [ real real] quaternion
+_{q}
: number* quaternion
_{q}
: number+ quaternion
*_{q}
: number* quaternion
/_{q}
: number+ quaternion
dotproduct
: number number real
crossproduct
: number number quaternion
conjugate
: number quaternion
exp_{q}
: number quaternion
log_{q}
: number quaternion
expt_{q}
: number number quaternion
sqrt_{q}
: number quaternion
sin_{q}
: number quaternion
cos_{q}
: number quaternion
tan_{q}
: number quaternion
asin_{q}
: number quaternion
acos_{q}
: number quaternion
atan_{q}
: 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, McGrawHill, 2nd ed, 1960.

[3] 
William Rowan Hamilton, On Quaternions; or on a new System of Imaginaries in Algebra, Philosophical Magazine, 18441850.

[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 160164.

^{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 3dimensional quantities. They are not be confused
with Scheme's onedimensional 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
twoargument atan
,
makerectangular
and makepolar
are not defined
for quaternions, but they are not defined for complex
numbers either.
^{7} Following Scheme practice, we use log to mean log_{e} rather than log_{10}.
^{8} sinh
and cosh
for reals are not
part of the Scheme standard [4] but they
can be readily defined using the
identities