Courses/CS 332L/Classes

From CSWiki

Jump to: navigation, search

[edit] Defining and building classes.

Let's create a way for users to define class-like data structures. Let's define the statement

class name(var1, var2, ..., varn))

to mean that the user is defining a class-like data structure named name with instance variables named var1, var2, ..., varn.

Once such a data structure is defined, the user might use it as in the following example.

?- eval(y = 5; class c1(a, b); x = new c1(3+y, 4*y), [], Bindings_out, Value).

Bindings_out = [x = c1 [a = 8, b = 20], c1 = class[a, b], y = 5]
Value = c1 [a = 8, b = 20]

To make this work, we will have to write code to recognize and store class definitions. Where will you store class definitions? The easiest solution is to store them in the Bindings list as illustrated. But that requires that the Bindings list always be available. As we will see below, that may not be the case. So let's store class definitions in a separate list, similar to the Bindings list. As with the Bindings list we will have two lists, Classes_in and Classes_out as in this revised version of the previous example.

?- eval(y = 5; class c1(a, b); x = new c1(3+y, 4*y), [], Classes_out, [], Bindings_out, Value).

Classes_out = [c1 = class[a, b]]
Bindings_out = [x = c1 [a = 8, b = 20], y = 5]
Value = c1 [a = 8, b = 20]

As illustrated class instances and their values can also be stored as a Bindings list but one associated with variables. The variable x is an instance of the c1 class. It's components are [a = 8, b = 20].

We will also have to write code to recognize new as a keyword that triggers tuple building. To build a new class, we will have to evaluate the arguments, retrieve the class definition and construct the new data structure consisting of the class name and its arguments. As shown above, we also define class as a unary prefix operator and we define each class name as a unary prefix operator as we see it. The precedence of class as an operator and c1 and the other class names as operators should be less than the precedence of =.

[edit] Retrieving instance components.

Once we are able to define and build classes the next thing to do is to retrieve components from their instances. To do that we need a notation to refer to instance components.

If c1 is defined as above and

x = new c1(3, 4) 

is a class instance, the standard notation is to use dot notation to retrieve x's components.

y = x.a

should assign 3 to y.

The dot (.) in prolog is already defined to mean a list. So to avoid confusion, let's use @ to refer to instance components. Instead of the above, we will write

y = x@a

to access the a component of x.

In current SWI prolog @ is defined as follows.

?- current_op(P, A, @). 

P = 200
A = fy 

That's close to what we want, but not exactly. A precedence of 200 is about right. We want @ to bind tightly. But we want @ to be a binary operator, and we want it to associate to the left. We want

x@y@z

to mean

(x@y)@z

That is: take the y component of x and then take the z component of the result.

So we must define

:- op(200, yfx, @).

Once we have @ defined properly, we can use it as follows.

?- eval(class c1(a, b); x = new c1(3, 4); y = x@a, [], Classes_out, [], Bindings_out, Result).

Classes_out = [c1 = class [a, b]]
Bindings_out = [y = 3, x = c1 [a = 3, b = 4]]
Result = 3

[edit] Defining eval for x@a

The real work of extracting a value from a data structure is done by the eval/6 (formerly eval/4) clause that evaluates x@a.

eval(X@A, Classes_in, Classes_out, Bindings_in, Bindings_out, Value) :-
  ...

The job is to do the following. Let's assume we have a class as defined above: class c1(a, b) and that we are evaluating

?- eval(class c1(a, b); x = new c1(3, 4); y = x@a, [], Classes_out, [], Bindings_out, Value)

Since we are representing a class instance as a list of components and their values, that is

x = c1 [a = 3, b = 4]

eval/6 will be passed arguments as follows.

eval(x@a, [c1 = class [a, b]], Classes_out, [x = c1 [a = 3, b = 4]], Bindings_out, Value) 

In this case, we take two steps.

  1. Evaluate x (by looking it up in Bindings_in). We will find that the value of x is c1 [a = 3, b = 4]. (Note that we don't need to look at Classes_in. That's used only when we perform a new operation.)
  2. Select the a component of c1 [a = 3, b = 4]. To do that we want to evaluate a with [a = 3, b = 4] as the Bindings_in. We can do that simply be calling eval/6 again, but this time using x's instance variables as the bindings.
eval(a, [c1 = class [a, b]], _Classes_out, [a = 3, b = 4], _Bindings_out, Value)

We don't care what the _Classes_out or _Bindings_out are, but we do care about the Value, which will be 3. So the point is that we can use the existing eval/6 mechanism but call it with the instance variables of the instance we are working with as the Bindings_in.

Personal tools