Exporting predicate as an operator

Which one of 1, 2, 3 below is correct to export a predicate in module 0 with operator declaration ? I think 3 is correct. Once 1 caused compile error. 2 seems OK, but also redundant. 3 seem to work as far as my trials. I am not familiar with exporting operators. Real case is complicated for others to reproduce here.

(0)

:- module(m, []).
:- op(500, fy, a).
a(X).

(1)

:- module(m, [a/1, op(500, fy, a)]).
:- op(500, fy, a).
a(X).

(2)

:- module(m, [(a)/1, op(500, fy, a)]).
a(X).

(3)

:- module(m, [op(500, fy, a)]).
a(X).

I remember this being really hard the first time I did it getting it to work and still do not consider exporting/importing operators a friend.

One thing I do remember was that operators need to be thought of as an entry into the operator table using op/3 and also as a predicate thus the need of the predicate indicator. So 2 would be my answer. The way I eventually came to that reasoning when first learning to use operators was by using listing/N to look at generated code of working code in the SWI-Prolog repository on GitHub. If however none of the possible answers is correct then again when Jan W. replies I will have learned something.

Operators and predicates are not related, so you need to export both. (2) is the correct answer.

Can you add more detail to the meaning of that, thanks.

An operator defines a human friendly syntax for a term with arity 1 or 2 as op Arg, Arg1 op Arg2 or Arg Op, This has no relation to a predicate. For example, + is an operator, but there is no predicate +/2. Some people seem to confuse (compound) terms and predicates. They are unrelated. A term is simply a data structure. A predicate links to executable code. For example, p(1) is just data. Only if you use it in the body position of a clause or give it to call/1 it gets related to the predicate p/1.

4 Likes

Thanks Jan and Eric

Now, due to the answer, queries on zdd gets more easy to write by exporting (zdd)/1 and op(1150, fy, zdd) than before like this.

% ?- zdd((append([], [], X))).    % before
%@ X = [].
% ?- zdd append([], [], X).   % after
%@ X = [].

% ?- zdd((true, true)).    % before
% ?- zdd true, true.      % after

You can think of the op/3 directive as something that modifies the current_op/3 data (which the parser uses). If an op/3 term appears in a module declaration, that does a implicit op/3 directive. You can look at all the operators by:

?- forall(current_op(Precedence, Type, Name), 
  writeln(current_op(Precedence, Type, Name))).

Your comment explains well, I think, why paren like (a)/1 is necessary in the exporting list. Thanks.

I have read what help(op/3) explains for the first time, and now understand clearly the meaning the symbol x y in operator type.

BTW, I took a ridiculous reading at first the word “identical” that, for example, op(10, xfy, [a, b]) makes a==b is true. Of course, I noticed right after “identical” means that the atom a and b have the same precedece 10 and the type xfy.