7.3.2 C:   OMac
On this page:
7.3.2.1 Objects
7.3.2.1.1 Example
7.3.2.1.2 The Self Parameter
7.3.2.1.3 Choice of Base Language
7.3.2.2 Classes
7.3.2.3 Stencil Code
7.3.2.4 Submission
7.3.2 C: OMac

    7.3.2.1 Objects

      7.3.2.1.1 Example

      7.3.2.1.2 The Self Parameter

      7.3.2.1.3 Choice of Base Language

    7.3.2.2 Classes

    7.3.2.3 Stencil Code

    7.3.2.4 Submission

In this assignment, we will build a series of object systems using macros, hence the name.You’ll also get a good workout...of your knowledge of objects. Write this code in Racket, i.e., #lang racket. You are of course welcome to use the SMoL compatibility layers.

7.3.2.1 Objects

You will define a macro for objects, and a macro to call them. Objects will have both fields and methods:
(object (fields [fn iv] ...)
        (methods [mn imp] ...))
 
(call o mn args ...)
where fn is a field name (symbol), mn is a methd name (also a symbol), and iv (“initial value”), imp (“implementation”), o (“object”), and args (“arguments”) are all expressions.If you haven’t already, you will need to learn about internal keywords: the () that goes in a macro definition header. Inside these parentheses you can name symbols that have to appear in the precisely named places in the input. This helps you write a macro that expects the keywords fields and methods.

Think of the object form as being roughly analogous to a JavaScript literal object (JSON).

There are several ways to represent objects internally. We recommend that you create a structure that stands for objects. (How many fields this has, and what they contain, is up to you. It can be richly structured or it could just be simple container.) The reason for this is, when you get to classes, you may accidentally pass classes where you intended objects or vice versa. If the two have a similar representation, then you could get very confusing output. In contrast, having explicit structures has two advantages. First, it catches confusion early. Second, in the macro you have to choose between accessors for the object and the field structures, forcing you to reflect on what should go there, thereby improving your understanding.

If a method is not found, raise an error. Field errors can be handled directly by Racket.

In this design, only methods can be accessed from outside an object; fields are effectively private, and can only be accessed by methods from inside the object. You could imagine extending it to access fields also.

Methods are explicitly declared as functions (typically defined using a lambda in place, but the function could also be defined outside and referenced from inside the object). All methods must take at least one argument; the first argument is the object itself. Conventionally, this argument would be called self or this. The call macro is responsible for passing the object along as the first parameter.

Be careful when implementing a complex construct like this. It’s possible to create an implementation that works most of the time but is subtly wrong (as in, does something undesirable). Think about how features can interact.

7.3.2.1.1 Example
(define cowboy-object
  (object
   (fields [name "Timmy the Cowboy"])
   (methods
    [say-howdy-to
     (lambda (self to)
       (string-append name " says: Howdy " to "!"))]
    [get-name
     (lambda (self) name)]
    [set-name
     (lambda (self x) (set! name x))])))
 
(test (call cowboy-object say-howdy-to "Partner")
      "Timmy the Cowboy says: Howdy Partner!")
7.3.2.1.2 The Self Parameter

There are generally two schools of thought on handling the “self” argument. In Java, the name this is bound automatically, and does not show up in the headers. In contrast, in Python, the name is not chosen by the language, and the programmer explicitly chooses it.We could design our object macro to automatically bind a name, too. There are multiple ways of doing it in Racket using features slightly more advanced than what we’ve used in this course. The latter design arguably has less “magic”. However, it means that a method declaration always has one more field than a corresponding call to the same method. Relatedly, it may actually be more confusing to students.

7.3.2.1.3 Choice of Base Language

Can you see why we asked you to program in Racket rather than Plait? What would go wrong? Try a small experiment (you can copy most of your code over) in Plait and see for yourself. (The answer is not something simple like a missing library, but rather a bit deeper. The simplest test may not reveal it. Remember that the primary difference between the two lies in Plait’s type system.)

Be as concise as possible in your answer. You should be able to give a short, illustrative response.

7.3.2.2 Classes

Now we’ll upgrade from objects to classes. (You are welcome to use your object macro when defining classes, but not required to.) Class definitions take the form
(class class-name
  (fields [fn iv] ...)
  (methods [mn imp] ...))
 
(new class-name)
where a class-name is represented as a symbol.

As discussed earlier, we recommend creating a structure to represent classes.

Fields and methods are just as before. Calling new on a class’s name creates an instance of the class, which is an object. Thus, in the following example:
(class Cowboy
  (fields [name "Timmy the Cowboy"])
  (methods
   [say-howdy-to
    (lambda (self to)
      (string-append name " says: Howdy " to "!"))]
   [get-name
    (lambda (self) name)]
   [set-name
    (lambda (self x) (set! name x))]))
 
(new Cowboy)
creates an object that is effectively equivalent to the one in Example.

7.3.2.3 Stencil Code
7.3.2.4 Submission

Form