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