Get and put methods are generated automatically for each of a class's public slots. These are 1-argument messages, named after the slots.
In the point class whose definition begins with
:- class point = [public x:float=0, public y:float=0].
the get and put methods are automatically generated for the x
and
y
slots. If the class defines a create/0
method, then the command
| ?- create(point, PointObj), PointObj >> x(OldX), PointObj >> y(OldY), PointObj << x(3.14159), PointObj << y(2.71828).
creates a point object and binds both OldX
and OldY
to
0.0E+00, its initial slot values. Then, it changes the values of the
x
and y
slots to 3.14159 and 2.71828, respectively. The
variable PointObj
is bound to the point object.
It is possible, and sometimes quite useful, to create get and put
methods for slots that do not exist. For example, it is possible to
add a polar coordinate interface to the point class by defining
get and put methods for r
and theta
, even though there are no
r
and theta
slots. The get methods might be defined as
follows:
Self >> r(R) :- Self >> x(X), Self >> y(Y), R2 is X*X + Y*Y, sqrt(R2, R). Self >> theta(T) :- Self >> x(X), Self >> y(Y), A is Y/X, atan(A, T).
This assumes that library(math)
, which defines the sqrt/2
and
atan/2
predicates, has been loaded. The put methods are left as an
exercise.
In the rational number class whose definition begins with
:- class rational = [public num:integer, public denom:integer].
get and put methods are automatically generated for the num
and denom
slots. It might be reasonable to add a get method for float
, which
would provide a floating point approximation to the rational in
response to that get message. This is left as an exercise.
It is also possible to define get and put methods that take more than one argument. For example, it would be useful to have a put method for the point class that sets both slots of a point object. Such a method could be defined by
Self << point(X,Y) :- Self << x(X), Self << y(Y).
Similarly, a 2-argument get method for the rational number class might be defined as
Self >> (N/D) :- Self >> num(N), Self >> denom(D).
Note that the name of the put message is (/)/2
, and that the
parentheses are needed because of the relative
precedences of the >>
and /
operators.
Put messages are used to store values in slots. Get messages, however,
may be used either to fetch a value from a slot or to test whether a
particular value is in a slot. For instance, the following command
tests whether the do_something/2
predicate sets the point
object's x
and y
slots to 3.14159 and 2.71828, respectively.
| ?- create(point, PointObj), do_something(PointObj), PointObj >> x(3.14159), PointObj >> y(2.71828).
The fetch_slot/2
predicate can similarly be used to test the value
of a slot.
The effects of a put message (indeed, of any message) are not undone upon backtracking. For example, the following command fails:
| ?- create(point, PointObj), PointObj << x(3.14159), PointObj << y(2.71828), fail.
But, it leaves behind a point object with x
and y
slots
containing the values 3.14159 and 2.71828, respectively. In this,
storing a value in an object's slot resembles storing a term in the
Prolog database with assert/1
.
Some care is required when storing Prolog terms containing unbound variables in term slots. For example, given the class definition that begins with
:- class prolog_term = [public p_term:term]. Self <- create.
the following command would succeed:
| ?- create(prolog_term, TermObj), TermObj << p_term(foo(X,Y)), X = a, Y = b, TermObj >> p_term(foo(c,d)).
The reason is that the free variables in foo(X,Y)
are renamed when the
term is stored in the prolog_term
object's p_term
slot. This is
similar to what happens when such a term is asserted to the Prolog
database:
| ?- retractall(foo(_,_)), assert(foo(X,Y)), X = a, Y = b, foo(c,d).
However, this goal would fail, because c
and d
cannot be unified:
| ?- create(prolog_term, TermObj), TermObj << p_term(foo(X,X)), TermObj >> p_term(foo(c,d)).