Tag Archives: proxies

Using both object proxies and regular objects

During the last three days I was working with Artur Miguel Dias, the author of CxProlog, rewriting P-FLAT, a Prolog toolkit for teaching Formal Languages and Automata Theory, as a Logtalk application, tentatively named L-FLAT. We made great progress and the final version of L-FLAT is going to be smaller, more portable, and both simpler to maintain and extend than the original plain Prolog application. One of our goals is to allow users to use both object proxies and regular objects when representing Turing Machines, Pushdown Automata, Context Free Grammars, and other concepts and mechanisms supported by L-FLAT. This flexibility allows users to represent e.g. a simple Finite Automaton with a small number of states and transitions as an object proxy (i.e. as a Prolog compound term; see my previous post) that might be entered interactively at the command-line:

fa(fa1,
    1,                                    % initial state
    [1/a/1, 1/a/2, 1/b/2, 2/b/2, 2/b/1],  % transitions
    [2]                                   % final states
).

It also allows users to represent e.g. a complex Turing Machine with dozens of transitions as a regular object, defined in a source file:

:- object(copyTM,
    instantiates(tm)).
 
    initial(q0).
 
    transitions([
        q0/'B'/'B'/'R'/q1,
        q1/a/'X'/'R'/q2,   q1/b/'Y'/'R'/q5,   q1/'B'/'B'/'L'/q7,
        q2/a/a/'R'/q2,     q2/b/b/'R'/q2,     q2/'B'/'B'/'R'/q3,
        q3/a/a/'R'/q3,     q3/b/b/'R'/q3,     q3/'B'/a/'L'/q4,
        q4/a/a/'L'/q4,     q4/b/b/'L'/q4,     q4/'B'/'B'/'L'/q4,
                           q4/'X'/'X'/'R'/q1, q4/'Y'/'Y'/'R'/q1,
        q5/a/a/'R'/q5,     q5/b/b/'R'/q5,     q5/'B'/'B'/'R'/q6,
        q6/a/a/'R'/q6,     q6/b/b/'R'/q6,     q6/'B'/b/'L'/q4,
        q7/'X'/a/'L'/q7,   q7/'Y'/b/'L'/q7
    ]).
 
    finals([]).
 
:- end_object.

Using both object proxies and regular objects requires that all predicates that expect e.g. a Regular Expression to accept both an object identifier and an object proxy (a compound term) as argument. This might sound as an additional hurdle until you realize that an object proxy is also an instantiation of the identifier of a parametric object. As long as the parametric object defines the straightforward predicates that give access to its parameters, everything will work as expected. But how do we relate the regular objects and the parametric objects? Easy. If you want to represent e.g. some finite automata as instances of class fa and other finite automata as object proxies, simply define a parametric object as an instance of class fa. The parametric object parameters will be the properties that might be unique for a specific finite automaton. The parametric object define the predicates that give access to these properties. These predicates are the same that are used in class fa in the definition of all predicates that need access to finite automaton properties:

:- object(fa(_Initial, _Transitions, _Finals),
    instantiates(fa)).
 
    initial(Initial) :-
        parameter(1, Initial).
 
    transitions(Transitions) :-
        parameter(2, Transitions).
 
    finals(Finals) :-
        parameter(3, Finals).
 
:- end_object.

Thus, using both object proxies and regular objects becomes trivial, fully transparent, and just a matter of defining the necessary parametric objects.

Share/Bookmark

Efficient representation of data objects

Some Logtalk applications need to represent large numbers of immutable objects. These objects typically represent data that must be validated or used for data mining. This kind of data is often exported from databases and must be converted into Logtalk objects for further processing. The application logic is usually represented by a set of hierarchies with the data objects at the bottom. Although data conversion is most of the time easily scriptable, the resulting objects can take up significantly more space than a straightforward representation as Prolog facts. Logtalk object representation is simply not optimized for this kind of objects. Fortunately, this is one of those cases where you can eat the cake and keep it. The solution is to represent data as compact Prolog facts and to interpret those facts as object proxies. An object proxy is simply a Prolog compound term that can be interpreted as a possible instantiation of the identifier of a parametric object.

For a toy example, assume that your data represents geometric circles with attributes such as the circle radius and the circle color:

% circle(Id, Radius, Color)
circle('#1', 1.23, blue).
circle('#2', 3.71, yellow).
circle('#3', 0.39, green).
circle('#4', 5.74, black).
circle('#5', 8.32, cyan).

We can define the following parametric object for representing circles:

:- object(circle(_Id, _Radius, _Color)).
 
    :- public([
        id/1, radius/1, color/1,
        area/1, perimeter/1
    ]).
 
    id(Id) :-
        parameter(1, Id).
 
    radius(Radius) :-
        parameter(2, Radius).
 
    color(Color) :-
        parameter(3, Color).
 
    area(Area) :-
        ::radius(Radius),
        Area is 3.1415927*Radius*Radius.
 
    perimeter(Perimeter) :-
        ::radius(Radius),
        Perimeter is 2*3.1415927*Radius.
 
:- end_object.

The circle/3 parametric object provides a simple solution for encapsulating a set of predicates associated with a given compound term. But how do we perform computations with the object proxies? We cannot send messages to Prolog facts! Or can we? Logtalk provides a handy notation for working with object proxies. For example, in order to construct a list with all circle areas we can write:

| ?- findall(Area, {circle(_, _, _)}::area(Area), Areas).
 
Areas = [4.75291, 43.2412, 0.477836, 103.508, 217.468]
yes

The message sending control construct, ::/2, accepts as receiving object the term {Proxy}. Logtalk proves Proxy as a Prolog goal and, if successful, sends the message to the resulting term. Backtracking over the Proxy goal is supported, allowing all matching object proxies to be processed by a simple failure-driven loop (as implicit in the findall/3 call above).

You can find the full source code of this example in the current Logtalk distribution. In real applications, data objects, represented as object proxies, are tied to large hierarchies representing both knowledge about data and how to reason with it.