The task of designing generic arithmetic operations is analogous to
that of designing the generic complex-number operations. We would
like, for instance, to have a generic addition
function
`add` that
acts like ordinary primitive addition `+` on ordinary numbers,
like `add_rat` on rational numbers, and like `add_complex` on
complex numbers. We can implement `add`, and the other generic
arithmetic operations, by following the same strategy we used in
section 2.4.3 to implement the generic selectors for
complex numbers. We will attach a type tag to each kind of
number and cause the generic
function
to dispatch to an appropriate
package according to the data type of its arguments.

The generic arithmetic functions are defined as follows:

function add(x, y) { return apply_generic("add", list(x, y)); } function sub(x, y) { return apply_generic("sub", list(x, y)); } function mul(x, y) { return apply_generic("mul", list(x, y)); } function div(x, y) { return apply_generic("div", list(x, y)); }

function install_javascript_number_package() { function tag(x) { return attach_tag("javascript_number", x); } put("add", list("javascript_number", "javascript_number"), (x, y) => tag(x + y)); put("sub", list("javascript_number", "javascript_number"), (x, y) => tag(x - y)); put("mul", list("javascript_number", "javascript_number"), (x, y) => tag(x * y)); put("div", list("javascript_number", "javascript_number"), (x, y) => tag(x / y)); put("make", "javascript_number", x => tag(x)); return "done"; }

function make_javascript_number(n) { return get("make", "javascript_number")(n); }

Now that the framework of the generic arithmetic system is in place, we can readily include new kinds of numbers. Here is a package that performs rational arithmetic. Notice that, as a benefit of additivity, we can use without modification the rational-number code from section 2.1.1 as the internal functions in the package:

function install_rational_package() { // internal functions function numer(x) { return head(x); } function denom(x) { return tail(x); } function make_rat(n, d) { let g = gcd(n, d); return pair(n / g, d / g); } function add_rat(x, y) { return make_rat(numer(x) * denom(y) + numer(y) * denom(x), denom(x) * denom(y)); } function sub_rat(x, y) { return make_rat(numer(x) * denom(y) - numer(y) * denom(x), denom(x) * denom(y)); } function mul_rat(x, y) { return make_rat(numer(x) * numer(y), denom(x) * denom(y)); } function div_rat(x, y) { return make_rat(numer(x) * denom(y), denom(x) * numer(y)); } // interface to rest of the system function tag(x) { return attach_tag("rational", x); } put("make", "rational", make_rational); put("add", list("rational", "rational"), add_rational); put("sub", list("rational", "rational"), sub_rational); put("mul", list("rational", "rational"), mul_rational); put("div", list("rational", "rational"), div_rational); } function make_rational(n, d) { return (get("make", "rational"))(n, d); }

We can install a similar package to handle complex numbers, using the
tag `"complex"`. In creating the package, we extract from the table
the operations `make_from_real_imag` and `make_from_mag_ang`
that were defined by the rectangular and polar packages.
Additivity
permits us to use, as the internal operations, the same `add_complex`, `sub_complex`, `mul_complex`, and `div_complex`
functions
from
section 2.4.1.

function install_complex_package() { // imported functions from rectangular and polar packages function make_from_real_imag(x, y) { return get("make_from_real_imag", "rectangular")(x, y); } function make_from_mag_ang(r, a) { return get("make_from_mag_ang", "polar")(r, a); } // internal functions function add_complex(z1, z2) { return make_from_real_imag(real_part(z1) + real_part(z2), imag_part(z1) + imag_part(z2)); } function sub_complex(z1, z2) { return make_from_real_imag(real_part(z1) - real_part(z2), imag_part(z1) - imag_part(z2)); } function mul_complex(z1, z2) { return make_from_mag_ang(magnitude(x) * magnitude(z2), angle(z1) + angle(z2)); } function div_complex(z1, z2) { return make_from_mag_ang(magnitude(x) / magnitude(z2), angle(z1) - angle(z2)); } // interface to rest of the system function tag(z) { return attach_tag("complex", z); } put("add", list("complex", "complex"), (z1, z2) => tag(add_complex(z1, z2))); put("sub", list("complex", "complex"), (z1, z2) => tag(sub_complex(z1, z2))); put("mul", list("complex", "complex"), (z1, z2) => tag(mul_complex(z1, z2))); put("div", list("complex", "complex"), (z1, z2) => tag(div_complex(z1, z2))); put("make_from_real_imag", "complex", (x, y) => tag(make_from_real_imag(x, y))); put("make_from_mag_ang", "complex", (r, a) => tag(make_from_mag_ang(r, a))); return "done"; }

Programs outside the complex-number package can construct complex numbers either from real and imaginary parts or from magnitudes and angles. Notice how the underlying functions, originally defined in the rectangular and polar packages, are exported to the complex package, and exported from there to the outside world.

function make_complex_from_real_imag(x, y){ return get("make_from_real_imag", "complex")(x, y); } function make_complex_from_mag_ang(r, a){ return get("make_from_mag_ang", "complex")(r, a); }

downward,the outer tag that is used to direct it to the appropriate package is stripped off (by applying

In the above packages, we used `add_rat`, `add_complex`, and
the other arithmetic
functions
exactly as originally written.
Once these definitions are internal to different installation
functions,
however, they no longer need names that are distinct from each other:
we could simply name them `add`, `sub`, `mul`, and `div`
in both packages.

The problem is that the complex-number selectors were never defined for"complex"numbers, just for"polar"and"rectangular"numbers. All you have to do to make this work is add the following to thecomplexpackage:

put("real_part", list("complex"), real_part); put("imag_part", list("complex"), imag_part); put("magnitude", list("complex"), magnitude); put("angle", list("complex"), angle);

There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in Github.

2.5.1
Generic Arithmetic Operations