Traits

Author(s): Jose F. Morales.

Stability: [devel] Currently the subject of active development and research. Syntax may change without warning or deprecation period.


This package extends the Ciao module system with traits. This is a lightweight translation with no overhead w.r.t. traditional use of multifile declarations for hook predicates.

A trait is defined in Ciao as a collection of predicates that can be implemented for any functor. Functors can implement multiple traits.

This translation delegates on the underlying module system as much as possible, e.g., for dealing with undefined predicates. See Example code and translation for a complete example that shows syntax, some special cases, and the translation to plain clauses.

Some important notes on the translation:

  • internal argument order ensures that first-argument indexing is preserved

  • functors data is passed as an extra argument to implementation clauses as follows:
    • constants add no extra arguments
    • unary functors f(Datum) are passed as Datum
    • any other functor is passed unaltered

Usage and interface

Other information

Example code and translation

Example code:

:- module(trait_test, [], [traits, assertions]).

:- trait(gadget, [
    p1/0,
    p2/0,
    q/1,
    r/2
]).

:- impl(gadget, datum0).

(datum0 as gadget).p1.

(datum0 as gadget).p2 :- fail.

(datum0 as gadget).q(X) :- X = r1.

(datum0 as gadget).r(X, Y) :- X = r1, Y = r2.

:- impl(gadget, datum1/1).

(datum1(_) as gadget).p1.

(datum1(_) as gadget).p2 :- fail.

(datum1(E1) as gadget).q(X) :- X = r1(E1).

(datum1(E1) as gadget).r(X, Y) :- X = r1(E1), Y = r2.

:- impl(gadget, datum2/2).

(datum2(_,_) as gadget).p1.

(datum2(_,_) as gadget).p2 :- fail.

(datum2(E1,_) as gadget).q(X) :- X = r1(E1).

(datum2(E1,E2) as gadget).r(X, Y) :- X = r1(E1), Y = r2(E2).

% (tests)

:- use_module(library(aggregates), [findall/3]).

:- test trait_test(X) =>
     X = [[true,false,r1,    [r1,    r2]],
      [true,false,r1(e1),[r1(e1),r2]],
      [true,false,r1(e1),[r1(e1),r2(e2)]]].

:- export(trait_test/1).
trait_test([Y1,Y2,Y3]) :-
    row(datum0,Y1),
    row(datum1(e1),Y2),
    row(datum2(e1,e2),Y3).

row(A,Xs) :- findall(X,col(A,X),Xs).

col(A,X) :- ( (A as gadget).p1 -> X = true ; X = false ).
col(A,X) :- ( (A as gadget).p2 -> X = true ; X = false ).
col(A,X) :- (A as gadget).q(X).
col(A,[X,Y]) :- (A as gadget).r(X, Y).

which should be equivalent to:

:- module(trait_orig, [], [traits, assertions]).

% :- trait gadget { ... }.
:- discontiguous 'gadget.p1'/1.
:- multifile 'gadget.p1'/1.
:- discontiguous 'gadget.p2'/1.
:- multifile 'gadget.p2'/1.
:- discontiguous 'gadget.q'/2.
:- multifile 'gadget.q'/2.
:- discontiguous 'gadget.r'/3.
:- multifile 'gadget.r'/3.

% :- impl gadget for datum0.
'gadget.p1'(datum0) :- '<datum0 as gadget>.p1'.
'gadget.p2'(datum0) :- '<datum0 as gadget>.p2'.
'gadget.q'(datum0, X) :- '<datum0 as gadget>.q'(X).
'gadget.r'(datum0, X, Y) :- '<datum0 as gadget>.r'(X, Y).

'<datum0 as gadget>.p1' :- true.

'<datum0 as gadget>.p2' :- fail.

'<datum0 as gadget>.q'(X) :- X = r1.

'<datum0 as gadget>.r'(X, Y) :- X = r1, Y = r2.

% :- impl gadget for datum1/1.
'gadget.p1'(M) :- M = datum1(D), '<datum1/1 as gadget>.p1'(D).
'gadget.p2'(M) :- M = datum1(D), '<datum1/1 as gadget>.p2'(D).
'gadget.q'(M, X) :- M = datum1(D), '<datum1/1 as gadget>.q'(X, D).
'gadget.r'(M, X, Y) :- M = datum1(D), '<datum1/1 as gadget>.r'(X, D, Y).

'<datum1/1 as gadget>.p1'(_E1) :- true.

'<datum1/1 as gadget>.p2'(_E1) :- fail.

'<datum1/1 as gadget>.q'(X, E1) :- X = r1(E1).

'<datum1/1 as gadget>.r'(X, E1, Y) :- X = r1(E1), Y = r2.

% :- impl gadget for datum2/2.
'gadget.p1'(M) :- M = datum2(_,_), '<datum2/2 as gadget>.p1'(M).
'gadget.p2'(M) :- M = datum2(_,_), '<datum2/2 as gadget>.p2'(M).
'gadget.q'(M, X) :- M = datum2(_,_), '<datum2/2 as gadget>.q'(X, M).
'gadget.r'(M, X, Y) :- M = datum2(_,_), '<datum2/2 as gadget>.r'(X, M, Y).

'<datum2/2 as gadget>.p1'(datum2(_E1, _E2)) :- true.

'<datum2/2 as gadget>.p2'(datum2(_E1, _E2)) :- fail.

'<datum2/2 as gadget>.q'(X, datum2(E1, _E2)) :- X = r1(E1).

'<datum2/2 as gadget>.r'(X, datum2(E1, E2), Y) :- X = r1(E1), Y = r2(E2).

% (tests)

:- use_module(library(aggregates), [findall/3]).

:- test trait_test(X) =>
     X = [[true,false,r1,    [r1,    r2]],
      [true,false,r1(e1),[r1(e1),r2]],
      [true,false,r1(e1),[r1(e1),r2(e2)]]].

:- export(trait_test/1).
trait_test([Y1,Y2,Y3]) :-
    row(datum0,Y1),
    row(datum1(e1),Y2),
    row(datum2(e1,e2),Y3).

row(A,Xs) :- findall(X,col(A,X),Xs).

col(A,X) :- ( 'gadget.p1'(A) -> X = true ; X = false ).
col(A,X) :- ( 'gadget.p2'(A) -> X = true ; X = false ).
col(A,X) :- 'gadget.q'(A, X).
col(A,[X,Y]) :- 'gadget.r'(A, X, Y).