2 Dynamically-Typed Core
For example, predicate objects could be used to implement a bounded buffer abstraction:
The following diagram illustrates the inheritance hierarchy created by this example (the explicit inheritance link from the buffer object toobject
bufferisa
collection;field
elements(b@buffer); -- a queue of elementsfield
max_size(b@buffer); -- an integermethod
length(b@buffer) { b.elements.length }method
is_empty(b@buffer) { b.length = 0 }method
is_full(b@buffer) { b.length = b.max_size }
predicate
empty_bufferisa
bufferwhen
buffer.is_empty;method
get(b@empty_buffer) { ... } -- raise error or block caller
predicate
non_empty_bufferisa
bufferwhen
not(buffer.is_empty);method
get(b@non_empty_buffer) { remove_from_front(b.elements) }
predicate
full_bufferisa
bufferwhen
buffer.is_full;method
put(b@full_buffer, x) { ... } -- raise error or block caller
predicate
non_full_bufferisa
bufferwhen
not(buffer.is_full);method
put(b@non_full_buffer, x) { add_to_back(b.elements, x); }predicate
partially_full_bufferisa
non_empty_buffer, non_full_buffer;
buffer
is omitted):
Predicate objects increase expressiveness for this example in two ways. First, important states of bounded buffers, e.g., empty and full states, are explicitly identified in the program and named. Besides documenting the important conditions of a bounded buffer, the predicate objects remind the programmer of the special situations that code must handle. This can be particularly useful during maintenance phases as code is later extended with new functionality. Second, attaching methods directly to states supports better factoring of code and eliminates if
and case
statements, much as does distributing methods among classes in a traditional object-oriented language. In the absence of predicate objects, a method whose behavior depended on the state of an argument object would include an if
or case
statement to identify and branch to the appropriate case; predicate objects eliminate the clutter of these tests and clearly separate the code for each case. In a more complete example, several methods might be associated with each special state of the buffer. By factoring the code, separating out all the code associated with a particular state or behavior mode, we hope to improve the readability and maintainability of the code.
The syntax for a predicate object declaration is as follows:
predicate_decl ::= "predicate" name {relation} [field_inits] ["when" expr] ";"
Generated with Harlequin WebMaker