Getters & Setters are functions

Many object-oriented languages force you to manually define get_foo and set_foo functions for each class slot foo in order to control access to the slots and provide for future extensibility. For example, set_foo could be modified to keep a table of values of foo.

Dylan provides for this extensibility without having to write the accessor functions yourself. Consider a simple Dylan class:

define class <vehicle> (<object>)
  slot driver, required-init-keyword: driver:;

This creates two functions, driver and driver-setter. You can prevent driver-setter from being defined by adding the constant modifier to the slot declaration:

constant slot driver, required-init-keyword: driver:;

Under normal circumstances, both the getter and setter function will be inlined and optimized into direct accesses.

If you later discover that you can’t allow direct access to the driver slot, you can rename the slot and create two explicit functions named driver and driver-setter to replace it, thus changing the internal behavior without changing the API for your class. The following example maintains a hash table mapping drivers to their cars:

define constant *driver-to-car-map* = make(<table>);

define class <vehicle> (<object>)
  // The leading '%' is a convention for naming internal slots.
  slot %driver, required-init-keyword: driver:;

define method initialize
    (vehicle :: <vehicle>, #key, #all-keys)
  *driver-to-car-map*[vehicle.%driver] := vehicle;

// The name "driver" would be exported as the public API.
define constant driver = %driver;

define method driver-setter
    (driver, vehicle :: <vehicle>) => (value :: <object>)
  *driver-to-car-map*[driver] := vehicle;
  vehicle.%driver := driver;

Note that Dylan provides some handy syntactic sugar to make slot accessor functions look like members of a C structure:

let car = make(<vehicle>, driver: "Frank");

// These two expressions are semantically identical and return the same value.

// These three statements perform the same assignment.
car.driver := "Sally";
driver(car) := "Sally";
driver-setter("Sally", car);