Racket's object system lives in the library racket/class (part of racket). It is a class-based system with single inheritance, interfaces and mixins, in which a class is a first-class value. A class name conventionally ends in % (object%, frame%) and an interface name in <%> (printable<%>); the viewer colours those endings as types. The racket/gui library (windows and widgets) is built on it.
Defining a class
(class superclass-expr class-clause …) makes a class that extends the class superclass-expr, usually object%. (class* superclass-expr (interface-expr …) class-clause …) also names interfaces that the class implements. The clauses can come in any order:
| Clause | Meaning |
|---|---|
(init init-decl …) | initialisation arguments: values supplied by name when an object is made. A declaration is id, (id default-expr) or ((internal external) default-expr) |
(init-field init-decl …) | an init argument that is also a field with the same name |
(field (id default-expr) …) | fields: the state of each object, each with its starting value |
(inherit-field id …) | brings a field of the superclass into scope |
(init-rest id), (init-rest) | without it a class accepts no more by-position initialisation arguments than its init clauses name; with it, id receives the extra ones as a list, and with no id they are accepted and passed on to the superclass |
(inspect inspector-expr) | sets the inspector of the class (who can see its fields) |
(public id …), (public-final id …) | declares new methods that subclasses may (public) or may not (public-final) override |
(override id …), (override-final id …) | declares methods that replace a method of the superclass |
(pubment id …), (overment id …), (augment id …), (augride id …), (augment-final id …) | the augmenting kinds: a method that calls inner to let subclasses add to it (pubment, overment), and one that adds to the superclass's (augment, augride, augment-final) |
(private id …) | declares methods and fields visible only inside this class |
(abstract id …) | declares methods that subclasses must define; the class cannot be instantiated until they do |
(inherit id …), (inherit/super id …), (inherit/inner id …) | brings inherited methods into scope so they can be called directly; the other two also give access to super and inner calls |
(rename-super (internal external) …), (rename-inner …) | renames a method of the superclass, or its augmenting version, inside this class |
a definition: (define id expr), (define (id arg …) …) | a private variable or function of the class |
a method definition: (define/public (name arg …) body) and relatives | declares and defines a method in one clause; see below |
an expression, (super-new) | runs when the object is created. (super-new [init-id expr] …) initialises the superclass; every class must run it once |
The definition shorthands are define/public, define/override, define/pubment, define/overment, define/augment, define/augride, define/public-final, define/override-final, define/augment-final and define/private, as well as the plural forms public*, override* and so on that declare and define a list of methods at once. Inside a method, this is the object, and (super name arg …) calls the superclass's version of a method that this class overrides; (inner default-expr name arg …) calls the subclass's augmentation, or evaluates the default if there is none. this% is the class of the object.
#lang racket
(define animal%
(class object%
(init-field name) ; a field set when the object is made
(field [energy 10])
(define/public (speak) (format "~a makes a sound" name))
(define/public (eat n) (set! energy (+ energy n)))
(super-new)))
(define dog%
(class animal%
(init [tricks '()])
(define known tricks)
(define/override (speak)
(string-append (super speak) ": woof"))
(define/public (learn t) (set! known (cons t known)))
(super-new))) ; init args reach the superclass by name
(define d (new dog% [name "Rex"]))
(send d speak) ; "Rex makes a sound: woof"
(send d eat 5)
(get-field energy d) ; 15
super.Making and using objects
| Form | Meaning |
|---|---|
(new class-expr [init-id expr] …) | makes an object, giving init arguments by name |
(make-object class-expr arg …) | makes an object, giving init arguments by position |
(instantiate class-expr (arg …) [init-id expr] …) | both: positional arguments, then named ones |
(send obj method arg …) | calls a method. send/apply takes the arguments as a list; send/keyword-apply adds keywords; dynamic-send takes the method name as a value |
(send* obj (method arg …) …) | several calls on the same object, one after another |
(send+ obj (method arg …) …) | chained: each call's result becomes the object of the next |
(get-field field obj), (set-field! field obj value), (field-bound? field obj) | read, change and test a field from outside; dynamic-get-field and dynamic-set-field! take the name as a value |
(with-method ([id (obj method)] …) body) | binds id to a method of an object, to call it as a function |
(generic class-or-interface method), (send-generic obj generic arg …) | a generic procedure for a method name, and its use on any object that has the method |
(class-field-accessor class field), (class-field-mutator class field) | procedures to read and change a field of an instance of the class |
Fields and init arguments have an internal and an external name (((internal external) default)), so a subclass cannot change the meaning of a name that a superclass uses internally. Names can also be hidden from other code by define-member-name and define-local-member-name.
Interfaces
(interface (super-interface-expr …) name-clause …) lists method names; a class that implements the interface must have a public method with each name. A clause is id, (id contract-expr), or a form with #:public default-expr or #:override default-expr that supplies an implementation. interface* adds properties. A class implements an interface by listing it in class*. The library offers printable<%> and writable<%> for classes that print themselves.
(define shape<%> (interface () area name))
(define circle%
(class* object% (shape<%>)
(init-field r)
(define/public (area) (* 3.14159 r r))
(define/public (name) "circle")
(super-new)))
(is-a? (new circle% [r 1]) shape<%>) ; #t
Mixins and traits
A mixin is a function from a class to a subclass of it: (mixin (interface-expr …) (interface-expr …) class-clause …) requires the argument class to implement the first interfaces, and makes the result implement the second. (define logging-mixin (mixin () () (super-new) …)) then (logging-mixin base%). A trait is a set of methods and fields that can be added to a class, and combined and changed before use: (trait trait-clause …), trait-sum, trait-exclude, trait-exclude-field, trait-alias, trait-rename, trait-rename-field, and trait->mixin to apply one.
Predicates and reflection
object?, class?, interface?, generic?, is-a? (an object is an instance of a class or interface, subclasses included), subclass?, implementation?, interface-extension?, method-in-interface?, object=?, object-interface, class->interface, interface->method-names, field-names, object-method-arity-includes?, object-info, class-info and object->vector inspect classes and objects. Contracts for classes and objects (class/c, object/c, instanceof/c, is-a?/c, subclass?/c, implementation?/c, ->m, ->*m, ->dm) are in racket/class and described on Contracts.
Units
Units (racket/unit) are a second way to build components, linked by name instead of by inheritance. A signature lists names ((define-signature name (id …))); a unit says which signatures it imports and exports and holds the definitions: (unit (import sig …) (export sig …) body …) or (define-unit name (import …) (export …) body …). (compound-unit …) or define-compound-unit links units, and invoke-unit or define-values/invoke-unit runs one. Units are less common than classes and modules.
Not described
Known, not yet covered
- Reference: initialisation order and the rules for init arguments (
initwith external names and the order ofsuper-new), class contracts, serializable classes (define-serializable-class),class-sealandclass-unseal,racket/class'sprintable<%>methods and the details of units (their link graph, signatures with contracts,unit/sig) were not read in full. - Reference: the table above for units is from memory of the Racket Guide; the Reference's units chapter was not downloaded.
- Viewer: the clauses in a class are read by their keyword. A macro that expands into class clauses is coloured as a function call.
See also
- Structs – records and generic interfaces, the lighter way to define a type
- Contracts –
class/cand->m - Typed Racket – typed classes