7

Why is argument precendence of **/2 (xfx) and (^)/2 (xfy) not the same in Prolog?

This causes minor inconsistencies, such as the following:

?- X = 1, Y is 1 ^ -X.
X = Y, Y = 1.

and:

?- Y is 1 ** -1.
Y = 1.

but:

?- X = 1, Y is 1 ** -X.
ERROR: Syntax error: Operator priority clash
ERROR: X = 1, Y is 1 *
ERROR: ** here **
ERROR: * -X .
Wouter Beek
  • 3,307
  • 16
  • 29
  • Excellent find! (Takes some time to answer) Do you have some code in Prolog or another programming language where you write A^ -B or similar? – false Dec 09 '14 at 23:21
  • @false I stumbled upon this today while implementing [XML Schema Datatypes 1.1](https://github.com/wouterbeek/plXsd/blob/master/number/xsd_number_aux.pl#L146). – Wouter Beek Dec 09 '14 at 23:27
  • Ok, so you will have to put brackets around. Did you see this somewhere else? – false Dec 09 '14 at 23:32
  • And writing `sum(D / 10 ** I),` is not an option (just to sort out the situation)? – false Dec 09 '14 at 23:35
  • @lurker: `(**)/2` gives always a float, like `(/)/2`. GNU is conforming, same in SICStus, IF, Prolog IV, Minerva, YAP, Ciao, XSB ; well you get the picture. – false Dec 10 '14 at 01:09
  • @false, Yes, I was just noting the difference since we were on the topic, not meaning to imply that it was "strange". I never really looked at the difference between `(**)/2` and `^/2` before. And... I get the picture. :) – lurker Dec 10 '14 at 02:36
  • @false Thanks, it's indeed trivial to alter my code to make it work correctly. The example was meant to illustrate that this peculiarity may actually come up in practice. – Wouter Beek Dec 10 '14 at 06:58
  • 1
    @WouterBeek: You changed the code to `sum(D * 10 ^ -I),` which cannot work for `I > 0`, in ISO, since `I` is an integer, and 10 is also an integer. In that case it is guaranteed that the result will be an integer, too. But it is not, and thus it results in `type_error(float,10)`. Either use `**` as suggested above, or use `sum(D * 10 ^ -float(I))` or `sum(D * 10.0^ -I)`. Or use Horner. It could be better conditioned. I am not good at floats. – false Dec 11 '14 at 09:35

3 Answers3

2

Minor point: It's (^)/2 and not ^/2 to indicate that ^ is used as an operator and to make it valid Prolog syntax and a predicate indicator (7.1.6.6).

(**)/2 and (^)/2 are both evaluable functors (9), so they can be used for Arithmetic evaluation (8.7) with (is)/2 and Arithmetic comparison (8.7) with (=:=)/2, (<)/2 and the like. Their definitions differ slightly.

(**)/2 always gives back a float in the same way as (/)/2 always gives a float. (SWI does not follow the standard here, it has its own conventions).

?- X is 2**2.
   X = 4.0.
?- X is 2/2.
   X = 1.0.

(^)/2 is here to permit integer exponentiation which has become much more important with many systems now supporting arbitrarily large integers. Think of 2^2^X. That is, if both arguments are integers, the result is an integer as well, in the same way that (*)/2 handles this case.

?- X is 2^2, Y is 2*2.
   X = 4, Y = 4.
?- X is 2.0^2, Y is 2.0*2.
   X = 4.0, Y = 4.0.

In those cases where (^)/2 would give a real value with two integer arguments (like 2^ -1), a type error is produced, and then there are more errors for otherwise complex or undefined results.

(^)/2 was used for exponentiation for quite some time. An early use of the exponentiation operator is in D.H.D. Warren's Thesis of 1977 in the example for symbolic differentiation. (It is at least not mentioned in Philippe Roussel's 1975 manual). Throughout the thesis and the 1978 User's guide, the ~ character is used consistently where one would expect a ^ like in integers are restricted to the range -2~17 to 2~17-1 , ie. -131072 to 131071. The declaration was the following and is unchanged since 1982.

:- op(300, xfy, ~).  % 1977
:- op(200, xfy, ^).  % 1982 - today

From 1982 on, it was used in quantification of setof/3 and bagof/3 but also as lambdas in natural language parsers. For all these uses it already had the right associativity and priority. As an evaluable functor it was present in several systems.

The first system to use (^)/2 as an evaluable functor meaning power is probably C-Prolog.

Compared to this heritage, the (**)/2 appeared in Prolog relatively late, most probably inspired by Fortran. It was proposed for inclusion (N80 1991-07, Paris papers) shortly before the first Committee Draft (CD 1992). Systems provided it also as exp/2.

(**)/2 has the same priority as (^)/2 but does not have any associativity, which at first might strike as odd, since there are quite some cases, where it is common to have exponentiation twice. Most prominently, the Gaussian function in its simplest form

e-x2

Instead of using the constant e and exponentiation, a special evaluable functor exp/1 is provided. Above is thus written as exp(- X**2). In fact, also Wikipedia uses this notation. Given this functor, there are no needs for associativity in this common case.

Should there actually be one, I would be very interested to see it.

Compared to other systems it seems quite common to offer two kinds of exponentiation. Think of Haskell which has ^ and **.

To conclude: There does not seem to be a frequent case where nested float exponentiation is needed. So minimal support seems to be preferable.

false
  • 10,264
  • 13
  • 101
  • 209
  • 1
    Thanks for this insightful answer. However, the inconsistency I describe in my question is not about nested float exponentiation. It is about (non-nested) float exponentiation with negated exponent (e.g., `?- Y = 2, X is 2 ** -Y.`). Any thoughts on that case? – Wouter Beek Dec 11 '14 at 05:36
  • @WouterBeek: I thought I explained that: If you want `- X**2` to work as in math, then `2 ** -X` can **only** work if priorities are the same and `**` is right associative. I probably should make this clearer. – false Dec 11 '14 at 08:47
  • I now understand why `(**)/2` is not associative. But is there also a reason why the precedence of `(-)/1` is the same as that of `(**)/2` (both 200)? It seems to me that unary negation should always precede exponentiation (regardless of whether floats or integers are involved). – Wouter Beek Dec 11 '14 at 09:05
  • @WouterBeek: It is the same for `(^)/2` which works perfectly with `-` – false Dec 11 '14 at 09:15
  • Unary negation indeed works as expected when it occurs in the exponent of `(^)/2`. Unary negation in the exponent of `(**)/2` requires additional brackets. My claim would be that whether one has to introduce additional brackets or not should (ideally) not depend on whether one is interested in floats or integers. – Wouter Beek Dec 11 '14 at 09:26
  • @WouterBeek Now your argument is based on some intangible principles. – false Dec 11 '14 at 09:28
2

@false answered your first question.

The example you give is due to the following difference:

?- integer(-1).
true.

?- X = 1, integer(-X).
false.

and the following precedences:

?- current_op(X, xfx, **).
X = 200.

?- current_op(X, fy, -).
X = 200.
mat
  • 40,498
  • 3
  • 51
  • 78
1

The reason (not justification) for the inconsistency is that in the original standard from 1995 only **/2 was an arithmetic exponentiation operator, while ^/2 was only used for quantifying variables in bagof/3 and setof/3. For the latter use, it made sense to have right-associativity, so you could write X^Y^foo(X,Y,Z). Why **/2 was not given xfy associativity I don't know (it would have been consistent with Fortran, for instance).

The ^/2 as an exponentiation operator was added in a 2012 "corrigendum", without revising the syntax, leading to the current inconsistency.

But note that you can simply fix this yourself by adding a directive

:- op(200, xfy, **).

This is unlikely to cause any problems. Moreover, in many modern Prologs operator declarations are effective only locally in a module.

jschimpf
  • 4,904
  • 11
  • 24