This assignment builds on your previous work in C: OMac.
Classes created objects that are isolated from one other. Sometimes, however, we don’t want them to be entirely isolated. In Java and other object-oriented languages, statics introduce the distinction between fields common to all instances of a class and those that are specific to each instance.
(class cn (statics [sn is] ...) (fields [fn iv] ...) (methods [mn imp] ...))
Assume that the statics have different names from the fields, so we don’t have to worry about which ones takes priority over the others.
(class Cowboy (statics [cowboy-count 0]) (fields) (methods [mosey-into-town (lambda (self) (set! cowboy-count (+ 1 cowboy-count)))] [town-aint-big-enough (lambda (self) (string-append "This town ain't big enough for the " (number->string cowboy-count) " of us"))])) (define c1 (new Cowboy)) (define c2 (new Cowboy)) (call c1 mosey-into-town) (call c2 mosey-into-town) (test (call c1 town-aint-big-enough) "This town ain't big enough for the 2 of us") (test (call c2 town-aint-big-enough) "This town ain't big enough for the 2 of us")
(class cn extends sn (fields [fn iv] ...) (methods [mn imp] ...))
You will also need to provide a pre-defined class called RootThis corresponds to Java’s Object, which is poorly named: Object is a class, not an object! that can serve as the root class for classes that have no real parent. Root itself has no fields or methods. It also has no parent; therefore, you need to define Root “internally” rather than using the class macro. This choice is intentional: it shows that some definitions may use powers that the language’s creator has but the language’s user does not.
Fields of the parent class are not visible in the child
class. Each class can only see its own fields. This means the same
field name can be present at each level, and each of these fields is
different.If this seems confusing, what happens in
Method invocation (call) looks up the method in the given object. If it is not found there, invocation chains to the parent. This repeats until the method is found or the chain reaches Root, at which point it raises an error.
Hint: Observe that method invocation happens on objects, but classes extend classes. That means an instantiated object also needs to instantiate its own private parent object, and so on. Again, there’s a relatively easy way to do this that will work most of the time, but may fail (i.e., produce undesirable results) in rare cases. Think about features interacting!
(class cn extends sn (statics [stn is] ...) (fields [fn iv] ...) (methods [mn imp] ...))
When you’re done with this, you’ll have a pretty good approximation of (untyped) Java classes!
We have staged Inheritance with Statics by creating an intermediate deliverable in Inheritance. Other than statics, the two portions have exactly the same behavior. Some of you may find it easier to work in that order. Others may want to do it in one leap and then “subtract” out the statics portion to create a solution for Inheritance. If you feel that would be easier, you’re welcome to do it: we don’t counsel against it.
The name of this assignment is a riff on our SMoL languages and meant as a tribute to the great object-oriented language, Smalltalk. However, the language we have built up here is (unfortunately) not really Smalltalk: it’s much closer to an untyped version of Java. In Smalltalk, classes are objects too, enabling various operations on classes (known as reflection). Also, if every class is itself an object, and every object is an instance of a class, there can be classes whose instances are other classes; such classes are called metaclasses. This combination of ideas leads to a beautiful, almost mystical, class hierarchy that gives programmers enormous expressive power.