Get and Put Methods

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)).