1 of 17

Lecture 22

Introduction to Racket Object-Oriented Programming (OOP)

Programming Languages

UW CSE 341 - Autumn 2021

2 of 17

Racket Has Classes and Objects (In Case You’re Into That Sort of Thing)

  • Racket has an interesting, elegant class and object system
    • The language/libraries are not organized around it
      • CSE 341 used to use Ruby where everything is an object
    • It does interact well with Racket’s other features (scope, closures, dynamic typing, …)
  • Our goal: learn the essence of OOP in a non-Java-like setting
  • Along with Java and OCaml, completes all 4 combinations of:
    • static vs. dynamic typing
    • functional vs. object-oriented

3 of 17

Documentation

  • The Racket Guide Chapter on Classes and Objects
    • Covers the language features we will use and more
    • We will also cover some more general concepts

  • The Racket Reference Manual naturally has all the gory details

4 of 17

A Note on the Homework

Next homework is about understanding and extending an existing program that uses a bunch of unfamiliar features

  • Good practice
  • Quite different feel than other CSE 341 homeworks
  • Key skill is reading code to determine what you do (and do not!) need to understand

Emphasizes how OOP uses overriding to allow for extensibility

5 of 17

The Rules of Class-Based OOP

  1. An object is a value
  2. You use an object by sending it messages (aka calling methods)
  3. Each object has its own state
  4. Each object is an instance of a class
  5. An object’s class determines the object’s behavior
    • How an object responds to messages depends on its state and its class’ method definitions

6 of 17

Pure OOP

In “full” OOP (cf. Ruby, Smalltalk, ..):

  • Every value is an object
  • All object state is private to that object (can only send messages to object)

In Java:

  • Almost every value is an object (not numbers, booleans, null)
  • State can be public or private

In Racket:

  • Lots of values are not objects (use OOP only when a-good-fit-for-the-job)
  • State can be public or private but we will keep it private for style and learning

7 of 17

Racket Class Basics

Before we can create an object, we need a class to make instances of

Racket classes are anonymous first-class values (!!)

  • Recall: Anonymous first-class function: (lambda (x y) …)
    • Refer to it later by binding to a variable: (define f (lambda (x y) …)
    • Call a function to produce a result
  • Now: Anonymous first-class class: (class superclass …)
    • Refer to it later by binding to a variable: (define f% (class )
    • Ending variables bound to classes with % is just a stylistic convention
    • Use (new f% …) to create an object that is an instance of f%

8 of 17

See the code

The next several slides may make little sense without the example class we are defining for rational numbers but can be a complementary reference

9 of 17

Class definitions

(class superclass …) form may take some getting used to

  • superclass can be object%, a built-in class with no methods

Flavors of things in the …:

  1. Public methods (define/public (methodName arg1 arg2) …)
    • Will see other forms of method definitions (e.g., overriding) in section
  2. Private state with plain-old (define …)
    • An instance’s methods will be able to access this state
  3. init parameters (arguments used in initializing the object)
  4. Other expressions (evaluated when initializing the object)
  5. (super-new)

10 of 17

Defining object construction

  • init parameters indicate what clients pass in for initialization
    • Clients must pass them unless a default is provided
    • These parameters are available only during initialization (but can be saved in private state)
  • (super-new) must be called once to perform superclass initialization
  • The “stuff” in the class form other than methods is evaluated in order
    • Roughly “the whole thing is the object constructor”
    • Initialize private state variables
    • Perform whatever computation you want
    • Superclass initialization
    • Scope is like a letrec -- order matters

11 of 17

Doing object construction

Clients instantiate a class with (new foo% …) where the … provides initialization parameters by name.

  • Remember: foo% is just any expression that evaluates to a class

Result is an object that is an instance of the class

  • Remember: Something with state you can send messages to

12 of 17

Using objects

(send obj msg arg1 … argN)

msg is the name of a public method defined by the class that obj is an instance of

  • Can include inherited methods (defined in a superclass)
  • If there is no such method, then this is a dynamic error
    • For statically typed OOP, “no such method” is the key “bad thing” a sound type system prevents

13 of 17

Why private state

  • Can hide/restrict the ability to mutate state
  • Hide implementation details -- can change later in equivalent ways
    • Remember: hiding is key to abstraction and abstraction is key to robust software
  • Can provide [just] the getters/setters you want
    • Some more “boilerplate” code, but language could provide sugar
    • Getters/setters needn’t be field read/writes. Simple/silly example:

(define kelvin-temp …)

(define/public (set-celsius-temp! t)

(set! kelvin-temp (- t 273.15)))

(define/public (possible-temp?) (> kelvin-temp 0))

14 of 17

Our example: objects aren’t modules

rational% “works fine enough” but it has a “cheat” compared to our similar example with OCaml modules

  • Because state is private-to-an-object, add! is impossible* to implement without public get-numerator and get-denominator
    • A problem if we don’t want these getters public to everyone
    • * Okay, it’s possible by misusing ->string, but other examples exist
  • This is a classic problem with trying to use objects for ADTs
    • Objects are not modules! (OCaml modules didn’t have this issue!)
    • Some OOP languages have special features to control method access
    • Racket has a fancy solution using lexical scope and “member ids” -- in the provided code if you’re curious, but totally optional

15 of 17

this

this is a special word that means “the current object” (same as Java)

  • Makes sense only in the context of an object
    • Inside a method or private state or initialization

Sending messages to this makes a lot of sense (see isWhole, double!):

  • But in a convenient/confusing twist, Racket (like Java) allows omitting send this, so it looks the same as a function call
  • Different from a private helper function because of potential overriding

Can also use this like any other expression - pass “yourself” as an argument, store in a data structure, etc. (see double!)

16 of 17

Inheritance

In a subclass:

  • Superclass state is in instances of the subclass, but subclass code/methods cannot directly access it
  • Superclass methods are defined in the subclass
    • Can be called from subclass methods via (send this …)
    • Cannot omit the send this

But if subclass definition includes (inherit foo), two things change:

  • Subclass definition fails (at time of definition) if foo not a public method in superclass
    • (inherit foo) often good style because of earlier error detection
  • Subclass methods can use foo without the send this

17 of 17

What about overriding?

Yep, overriding is an essential aspect of OOP.

Stay tuned! See section and the next lecture.