Previous section: Defining New Classes

Dylan reference manual -- Slot Options

Slot Options

There are three kinds of slot-specs that are allowed in class definitions. These are slot specifications, initialization argument specifications, and inherited slot specifications.

Slot Specifications

A slot specification describes a slot.

The syntax of a slot specification is as follows: [ adjectives ] [ allocation ] slot getter-name [:: type ] #key setter init-keyword required-init-keyword init-value init-function

getter-name specifies the getter name for the slot. It must be a variable name.

The adjectives are words separated by spaces. Currently, the allowed adjectives are open and sealed. These adjectives control the level of dynamism of the getter, and (if present) the setter for this slot. See the Controlling Dynamism chapter for more details.

allocation controls the allocation of the slot. See the Specifying Allocation section for details.

The keywords setter, init-keyword, init-value, and init-function respectively specify a setter name, an init-keyword, and (if the allocation is not virtual) a default value specified with init-value: or init-function:. See the Keywords Allowed in Slot Specs section for more detail.

If there is an init-keyword specified with init-keyword: or required-init-keyword:, the slot is said to be keyword initializable

The following example defines a class with two keyword initializable slots accessed by the generic functions bar-x and bar-y.

define class <bar> (<object>)
  slot bar-x, init-keyword: x:;
  slot bar-y, init-keyword: y:;
end class <bar>;

In this example, the call make (<bar>, x: 2, y: 3) will make an instance in which these slots are initialized to 2 and 3 respectively; the call make (<bar>) will make in instance in which these slots are not initialized (attempting to get the values by calling the getter functions signals an error).

Initialization Argument Specifications

An initialization argument specification does not describe a slot; it describes how a particular initialization argument is to be handled. It allows the type of the initialization argument to be restricted, it allows the initialization argument to be declared to be required, and it allows the specification of a default value for an initialization argument which is not required.

There are two kinds of initialization argument specifications: required initialization argument specifications, and optional initialization argument specifications.

A required initialization argument specification has the form required keyword keyword #key type

A required initialization argument specification asserts that the initialization argument is required in a call to make -- the default make method will signal an error if no such initialization argument is supplied.

An optional initialization argument specification has the form keyword keyword #key type init-value init-function

An optional initialization argument specification can be used to specify a default value for the initialization argument. When a call to make does not specify the keyword, the default value is used by the default make method in computing the defaulted initialization arguments , and indirectly to initialize a slot. (See the next section on slot initialization for more details.)

The type argument has the same meaning in both kinds of initialization argument specification: It restricts the type of that initialization argument. (Note that this is not the same thing as restricting the type of the slot.)

The following example defines a class similar to the previous example class <bar>, but with modified initialization behavior.

define class <baz> (<bar>)
   required keyword x:;
   keyword y:, init-value: #t;
end class <baz>;

The x: initialization argument is specified as being required: make will signal an error if an x: keyword argument is not supplied. The y: initialization argument , and consequently the initial value for the bar-y slot, now defaults to #t if not specified in the call to make.

More than one keyword initializable slot may be initialized from the same initialization argument (i.e., more than one keyword initializable slot may specify the same init-keyword). However, an error is signaled if a single define-class form has more than one initialization argument specification for the same keyword. An error will also be signaled if a single define-class form has keyword initializable slots which specify init-value: or init-function: and an initialization argument specification for the same keyword which either requires a value (required-init-keyword:) or provides a default value with init-value: or init-function: -- i.e., the initial value specification for the slot can never be used.

Inherited Slot Specifications

An inherited slot specification specifies a getter generic function, and optionally an init-value: or init-function: slot option, but not both.

The syntax of an inherited slot specification is as follows:

inherited slot getter-name #key init-value init-function

getter-name identifies the slot. It must be a variable name. A superclass of the class being defined must specify a slot with the same getter.

If neither init-value: nor init-function: is specified, the only function of the inherited slot specification is to require that some superclass of the class being defined specify a slot with the same getter. It could also be useful as documentation, perhaps. Because the init-value: and init-function: slot options are not allowed for virtual slots, this is the only valid form of inherited slot specification for virtual slots.

If either init-value: or init-function: is specified, the superclass's slot's init-value: and init-function: slot options are ignored and the options in the inherited specification are used instead. This allows the init-value: and init-function: slot-options of an inherited slot to be replaced in a subclass so the default initial value of the slot can be changed.

define class <animal> (<object>)

slot n-legs, init-value: 4;

end class;

define class <spider> (<animal>)

inherited slot n-legs, init-value: 8;

end class;

Specifying Allocation

allocation in a slot specification specifies how storage for the slot is allocated. allocation should be one of the symbols instance, class, each-subclass, virtual, or constant, or it may be an implementation-dependent value. This argument is not evaluated.
instance
Indicates that each instance gets its own storage for the slot. This is the default.
class
Indicates there is only one storage location used by all the direct and indirect instances of the class. All the instances share a single value for the slot. If the value is changed in one instance, all the instances see the new value.
each-subclass
Indicates that the class gets one storage location for the slot, to be used by all the direct instances of the class. In addition, every subclass of the class gets a storage location for the slot, for use by its direct instances.
constant
Indicates a constant value for the slot. There will be no setter method. The value of the slot must be specified as an init-value.
virtual
Indicates that no storage will be automatically allocated for the slot. If allocation is virtual, then it is up to the programmer to define methods on the getter and setter generic functions to retrieve and store the value of the slot. Dylan will ensure the existence of generic functions for any specified getter and setter but will not add any methods to them.
The values of virtual slots are not automatically initialized when a new instance is created. The programmer must perform any necessary initialization. This would usually be done inside a method on initialize. Because the values of virtual slots are often computed from other values at run-time, many virtual slots will not require any explicit initialization.

To support the slot-initialized? protocol in a virtual slot, programmers must define a method for slot-initialized? that shares a protocol with the getter method for the slot.

Keywords allowed in slot-specs

Each keyword can be specified no more than once and must be followed by a value. Implementations may add additional keyword arguments.
setter:
The name of a module variable to which the setter method should be added, or #f if no setter method should be defined. This argument is not evaluated. If it is not supplied, it will default to the symbol whose name is the concatenation of getter-name and "-setter", unless the allocation of the slot is constant in which case it defaults to #f.
type:
Should be a type specifier that limits the types of values that can be stored in the slot. This type specification is enforced by the low-level slot storage mechanisms. It is not enforced for virtual slots, which do not use the low-level slot storage mechanisms.
This argument defaults to <object>, which allows any object to be stored in the slot. The argument is evaluated once, after the variable containing the class is defined and before the slot is first added to an instance.
init-value:
Supplies a default initial value for the slot. The argument is evaluated once, after the variable containing the class is defined and before the slot is first added to an instance. The resultant value is used as the initial value when an instance is created. If you want to create a new value for every instance, you should supply an init-function: rather than an init-value:. There is no default value for this argument.
init-value: may not be specified with init- function:.
init-function:
Should be a function of no arguments. It will be called to generate an initial value for the slot when a new instance is created. There is no default value for this argument. The argument is evaluated once, after the variable containing the class is defined and before the slot is first added to an instance.
init-function: may not be specified with init- value:.
init-keyword:
Should be a keyword. It permits an initial value for the slot to be passed to make, as a keyword argument using this keyword. For use, see "Slot Initialization," below. This argument is not evaluated.
There is no default for the init-keyword: argument. If none is specified, there will be no init-keyword for the slot.
init-keyword: and required-init-keyword: cannot both be specified for a single slot.
required-init-keyword:
Like init-keyword:, except it indicates an init-keyword that must be provided when the class is instantiated. If make is called on the class and a required init-keyword is not provided, an error is signaled.
If required-init-keyword: is specified for a slot, then init-keyword:, init-value:, and init-function: cannot be specified.

Filtered Slots

In some situations, the value of a slot will be stored directly in instances of one class but will require some computation in a subclass. For example, the position slot could be stored as a value in direct instances of <view> while requiring some computation in direct instances of the <view> subclass <displaced-view>.

Both classes provide the same interface to position (the position generic function). If the implementor of either class decides to change the implementation, users of the classes will not need to change or recompile code, because there is no change in the interface. Consistent function call syntax helps hides implementation details.

In this case, the <view> class supports position as an instance slot, and the <displaced-view> class supports position as a filtered slot. Methods for position and position-setter are added automatically by the slot definition in <view>; these methods access the raw value of the slot in the instance. In contrast, the implementor of <displaced-view> must explicitly add methods to the two generic functions. The <displaced-view> methods can call next-method to access (get or set) the stored value of the slot.

define class <view> (<object>)
  instance slot position;
  ...
end class;

define class <displaced-view> (<view>)
  ...
end class;

define method position (v :: <displaced-view>)
  displace-transform (next-method (v));
end method;

define method position-setter (new-position),
                               v :: <displaced-view>)
  next-method (undisplace-transform (new-position), v);
end method;

In other situations, a programmer will want storage in an instance for a slot value, but will want to perform some auxiliary action whenever the slot is accessed. In this case, the programmer should define two slots: an instance slot to provide the storage and a virtual slot to provide the interface. In general, only the virtual slot will be documented. The instance slot will be an internal implementation used by the virtual slot for storage. An example of such use would be a slot that caches a value.
define class <shape> (<view>)
  virtual slot image;
  instance slot cached-image, init-value: #f;
  ...
end class;

define method image (shape :: <shape>)
  cached-image (shape)
    | (cached-image (shape) := compute-image (shape));
end method;

define method image-setter (new-image, shape :: <shape>)
  cached-image (shape) := new-image;
end method;

Note that, as in the above example, if you define a virtual slot, you are expected to define an internal instance slot in order to provide a value for the slot accessor.

Next section: Instance creation