| < Free Open Study > |
|
In its simplest form, an object is just a data structure encapsulating some internal state and offering access to this state to clients via a collection of methods. The internal state is typically organized as a number of mutable instance variables (or fields) that are shared among the methods and inaccessible to the rest of the program.
Our running example throughout the chapter will be objects representing simple counters. Each counter object holds a single number and provides two methods (i.e., responds to two messages)—get, which causes it to return its current value; and inc, which increments the value.
A straightforward way of obtaining this behavior using the features we have discussed in previous chapters is to use a reference cell for the object's internal state and a record of functions for the methods. A counter object whose current state is 1 looks like this:
c = let x = ref 1 in {get = λ_:Unit. !x, inc = λ_:Unit. x:=succ(!x)}; ▸ c : {get:Unit→Nat, inc:Unit→Unit}
The method bodies are both written as functions with trivial parameters (written _ because we don't need to refer to them in the bodies). The abstractions block evaluation of the method bodies when the object is created, allowing the bodies to be evaluated repeatedly, later, by applying them over and over to the unit argument. Also, note how the state of this object is shared among the methods and inaccessible to the rest of the program: the encapsulation of the state arises directly from the lexical scope of the variable x.
To invoke a method of the object c, we just extract a field of the record and apply it to an appropriate argument. For example:
c.inc unit; ▸ unit : Unit c.get unit; ▸ 2 : Nat (c.inc unit; c.inc unit; c.get unit); ▸ 4 : Nat
The fact that the inc method returns unit allows us to use the ;-notation (§11.3) for sequences of increments. We could equivalently have written the last line above as:
let _ = c.inc unit in let _ = c.inc unit in c.get unit;
Since we may want to create and manipulate many counters, it is convenient to introduce an abbreviation for their type:
Counter = {get:Unit→Nat, inc:Unit→Unit};
Our attention in this chapter is focused on how objects are built, rather than on how they are used in organizing larger programs. However, we do want to see at least one function that uses objects, so that we can verify that it works on objects with different internal representations. Here is a trivial one—a function that takes a counter object and invokes its inc method three times:
inc3 = λc:Counter. (c.inc unit; c.inc unit; c.inc unit); ▸ inc3 : Counter → Unit (inc3 c; c.get unit); ▸ 7 : Nat
| < Free Open Study > |
|