I'm glad to know there's a module system of such sophistication. Also, whereas the chapter I was citing introduces the rational number by means of numerous Scheme procedures, it wouldn't make a lot of sense to go to all this trouble in PLT Scheme, where the fraction type is built in, e.g.:

(define a (/ 3 4)) a 3/4 (define b (/ 1 12)) (+ a b ) 5/6 (- a b) 2/3 (* a b) 1/16 (/ a b) 9

In Python, I'd do the same re modules: store the Fraction class in a module, then import. I happen to have one handy:

from mathobjects import Fraction

modules bear some resemblance to classes in that both use the dictionary structure to define a table of contents. You can get at the keys to a class with dir:

dir(Fraction) ['__abs__', '__add__', '__div__', '__doc__', '__eq__', '__float__', '__gt__', '__init__', '__lt__', '__module__', '__mul__', '__neg__', '__pow__', '__radd__', '__rdiv__', '__repr__', '__rmul__', '__sub__', 'denom', 'fformat', 'float', 'list', 'mkfract', 'numer', 'recip', 'simplify']

Same with a module:

import mathobjects dir(mathobjects) ['Fraction', 'Matrix', 'Poly', 'Sqmatrix', '__builtins__', '__doc__', '__file__', '__name__', '__path__', 'deriv', 'polynomial', 'pyfraction', 'simplematrix']

Then use the Fraction class to instantiate objects, a and b. Because the primitive operators have been overridden, your fraction objects behave as expected:

a = Fraction(3,4) b = Fraction(1,12) a+b (5/6.) a-b (2/3.) a*b (1/16.) a/b 9

It was my decision, in the __repr__ method, to have that little decimal point in the denominator. This allows conversion to float simply by evaluating a stringified representation:

eval(str(a+b)) 0.83333333333333337

although __float__ is also defined:

float(a+b) 0.83333333333333337

Using a parallel infrastructure, we would define polyhedra as another kind of object, based on a list of coefficients as parameters:

from mathobjects import Poly p = Poly([1,2,3,4]) p x**3 + 2*x**2 + 3*x + 4 q = Poly([3,-2,-1,-1]) p*q 3*x**6 + 4*x**5 + 4*x**4 + 3*x**3 - 13*x**2 - 7*x - 4 p-q -2*x**3 + 4*x**2 + 4*x + 5 p+q 4*x**3 + 2*x + 3

A fully generalized system would allow me to use polynomials in the numerator and denominator of Fractions, but so far my polynomial objects don't understand the / operator -- dividing polys is somewhat difficult, especially if you plan to simplify them (which involves finding common factors, i.e. the gcd). However, I *am* able to use the Fraction objects as coefficients if I like:

r = Poly([a,b,1]) r (3/4.)*x**2 + (1/12.)*x + 1 r*p (3/4.)*x**5 + (19/12.)*x**4 + (41/12.)*x**3 + (21/4.)*x**2 + (10/3.)*x + 4

And to pass values to the polynomial, as a function:

s = r*p s(10) (284437/3.) s(-12) -158976

Note that s(10) returned a fraction, not a decimal. This is because my evaluator processes coefficients as fractions, in order to make sure we don't loose any information as a result of casting to floating point. It was by building polynomials such as the above, and passing values to them, that I developed the Bernoulli numbers:

bernoulli.bernseries(14) [1, (1/2.), (1/6.), 0, (-1/30.), 0, (1/42.), 0, (-1/30.), 0, (5/66.), 0, (-691/2730.), 0, (7/6.)]

The Matrix object, with subclass Sqmatrix (square matrix) works the same way, with *, ** (power), +, - and / overrridden. The elements of a matrix may be Fractions or Polys. So the idea is to learn about mathematical objects by building them, just as we build radios or other models to learn about their design and structure. The result is, in this case, a primitive computer algebra system. In math class, we would spend more time talking about fractions, lcm, gcd, the determinant of a matrix and so on, and less time worrying about the syntax and infrastructure of Python, which presumably they picked up somewhat earlier -- maybe in 8th grade, whereas by this time we're in high school. I gather you take a similar approach with Scheme, focusing increasingly on the knowledge domain as time goes on, with Scheme itself receiving less emphasis. Kirby