Welcome to SWI-Prolog (threaded, 64 bits, version 9.0.4)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?- op(200,xf,-).
true.
?- current_op(P,M,-).
P = 200,
M = xf ;
P = 200,
M = fy ;
P = 500,
M = yfx.
?- +a- == (+a)- .
true. % This should be false.
?- current_op(P,M,+).
P = 200,
M = fy ;
P = 500,
M = yfx.
Since + is defined as a prefix operator and - as a postfix operator with the same precedence, +a- is ambiguous so you have to use parentheses to override the default. That’s also the safest thing to do.
In general, user defined operators can lead to ambiguities, and various Prolog’s may have different rules for resolving those ambiguities. '-' is particularly problematical because it’s also defined as a prefix and infix op, and can also be interpreted as an operand (the atom '-'). This raises all kinds of issues when parsing with limited lookahead. Note that if the operator '--' is used instead, you get the expected(?) result:
?- op(200,xf,--).
true.
?- X = +a-- , write_canonical(X).
+(--(a))
X = +a-- .
You are right. Using parentheses can eliminate ambiguity and also improves readability. But I do not see why +a- is ambiguous in this context. Due to my understanding, ‘+’ is an fy operator and ‘-’ is an xf operator in this expression. Both of them are at priority level 200. Thus +a and a- are also at priority level 200. Consequently, +a cannot be the parameter of the xf operator ‘-’ because the priority level of its parameter must be less than 200. But a- can be the parameter of the fy operator ‘+’ because the priority level of its parameter can be 200.
Your argument makes sense but it does require a lookahead of two tokens in the parser which may not be commonly implemented in Prolog parsers. I guess SWIP is one of those that doesn’t:
And I might be wrong but I think the ISO standard states that Prolog syntax be parseable with an LL1 parser.
Another data point from Eclipse documentation:
A.3.3 Operator Ambiguities
Unlike the canonical syntax, operator syntax can lead to ambiguities.
For instance, when a prefix operator is followed by an infix or postfix operator, the prefix is often not meant to be a prefix operator, but simply the left hand side argument of the following infix or postfix. In order to decide whether that is the case, ECLiPSe uses the operator’s relative precedences and their associativities, and, if necessary, a two-token lookahead. If this rules out the prefix-interpretation, then the prefix is treated as a simple atom. In the rare case where this limited lookahead is not enough to disambigute, the prefix must be explicitly enclosed in parentheses.
Another source of ambiguity are operators which have been declared both infix and postfix. In this case, ECLiPSe uses a one-token lookahead to check whether the infix-interpretation can be ruled out. If yes, the operator is interpreted as postfix, otherwise as infix. Again, in rare cases parentheses may be necessary to enforce the interpretation as postfix.
When a binary prefix operator is followed by an infix operator, then either of them could be the main functor. Faced with the ambiguity, the system will prefer the infix interpretation. To force the binary prefix to be recognised, the infix must be enclosed in parentheses.
Just a few indications of the state of parsing user defined operators in Prolog. So you shouldn’t be surprised by a few anomalies, particularly in the area of postfix operators which don’t seem to be used a lot.
If you’d like to help sort it out, SWIP is an open-source community .
THANKS! If I understand well, this problem is due to some acceptable limitation of the implementation. After all, I am surprised that Prolog allows a prefix and a suffix operator with the same name. But you are right. Brackets help.
?- current_op(P,A,-).
P = 200,
A = xf ;
P = 200,
A = fy ;
P = 500,
A = yfx.
?- string_termList("+a- .",[Term]), write_canonical(Term).
+(-(a))
Term = + -a.
and
?- current_op(P,A,-).
P = 200,
A = yf ;
P = 200,
A = fy ;
P = 500,
A = yfx.
?- string_termList("+a- .",[Term]), write_canonical(Term).
-(+(a))
Term = - +a.