Operators: Python meaning vs Prolog meaning¶
Many operator spellings exist in both worlds Clausal Prolog joins: Python and ISO Prolog. Which meaning applies depends on the surface and on how the operator is written:
- Bare, in seam syntax (
-7 // 2,2 ** 3): the Python-shaped syntax of.seamfiles, clause bodies and--expressions alike. A bare arithmetic operator keeps Python's meaning. - Quoted, or built as a cell (
'//'(-7, 2),'**'(2, 3), or a term built at runtime withunpack(T, ['//', -7, 2])): the operator follows Scryer Prolog, and through it ISO 13211-1. - Bare, in Clausal Prolog (
.clausalfiles): the operator follows Scryer, as a quoted one does. Clausal Prolog has no++, so Python's meaning is reachable only through a.seammodule.
The rule, in the operator's words: operators in seam syntax follow Python semantics unless quoted; quoted ones follow Scryer's.
Arithmetic¶
| Spelling | Bare, seam syntax: Python meaning | Quoted / cell: Scryer meaning | Bare, Clausal Prolog (.clausal) |
|---|---|---|---|
+ - * |
exact addition, subtraction, multiplication (a Decimal keeps its scale) |
the same | the same |
/ |
in evaluation (eval_, 'is', the ISO comparisons): Python true division — 7 / 2 is 3.5, 6 / 2 is 3.0; a Fraction or Decimal operand stays exact as in Python. Inside a constraint (==, <, ...): exact rational, X == 7 / 2 gives 7/2 and X == 6 / 2 gives 3 (CLP(ℚ), like Scryer's {X = 7/2}) |
Scryer's division, always a float: '/'(7, 2) is 3.5, '/'(6, 2) is 3.0; inside a constraint, rational as the bare one |
Scryer: a float in evaluation; rational inside {...} |
rdiv |
(no bare spelling) | the exact rational division: rdiv(7, 2) is 7/2, rdiv(6, 2) is 3, everywhere |
the same |
// |
Python floor division: -7 // 2 is -4 |
ISO integer division, truncating toward zero: '//'(-7, 2) is -3; integers only |
Scryer: truncates |
div |
(no bare spelling) | ISO floored division: div(-7, 2) is -4; integers only |
the same |
% |
Python modulo, sign of the divisor: -7 % 2 is 1 |
(no quoted %; write mod) |
% starts a comment; write mod (Python's % only in the seam) |
mod |
(no bare spelling) | ISO modulo, sign of the divisor: mod(-7, 2) is 1; integers only |
the same |
rem |
(none) | ISO remainder, the sign of the dividend: rem(-7, 2) is -1; integers only |
the same |
** |
Python power: 2 ** 3 is the integer 8; 2 ** -1 is 0.5 |
ISO power, always a float: '**'(2, 3) is 8.0 |
Scryer: a float |
^ |
Python bitwise XOR — as a CLP(B) formula in sat(X ^ Y); not evaluable by eval_ |
ISO integer power: '^'(2, 3) is 8; '^'(2, -1) is type_error(float, 2); '^'(1, -1) is 1 |
Scryer: integer power |
& \| ~ |
Python bitwise AND / OR / NOT — CLP(B) formulas in sat(...); not evaluable by eval_ (type_error(evaluable, (&)/2)) |
no ISO operator of these spellings; ISO's bitwise functors are the quoted '/\\'(A, B), '\\/'(A, B), '\\'(A) and xor(A, B), integers only ('/\\'(12, 10) is 8) |
Scryer: /\, \/, \, xor |
<< >> |
Python shifts; not evaluable by eval_ |
ISO shifts, integers only: '>>'(-7, 1) is -4; a negative count shifts the other way ('<<'(1, -1) is 0) |
Scryer: the same |
unary - |
negation | the same: '-'(5) is -5 |
the same |
A zero divisor in plain arithmetic is ISO's
evaluation_error(zero_divisor) on every spelling, naming the operator:
eval_(1 // 0, X) raises error(evaluation_error(zero_divisor), (//)/2), and
so do 'is' and the ISO comparisons ('=:=', '<', ...). A bare
Python-semantics operator raises it too, never a raw Python
ZeroDivisionError: it is a logic-level error that catch/3 sees.
A constraint (==, !=, <, ... — CLP(ℤ)'s #= family) is a relation,
and over an expression with no value it has no solutions: X == 1 // 0
fails, as in Scryer, in every goal order (X == 1 // Y, Y is 0 fails too),
and a divisor that becomes 0 during a search fails that branch.
Inside ++(...) the code is plain Python and every operator is Python's,
exceptions included.
Comparison and unification¶
These spellings already differ from Python in seam syntax: a clause body is logic, not Python.
| Spelling | Bare, seam syntax | Quoted: ISO / Scryer meaning |
|---|---|---|
== |
arithmetic equality posted as a constraint (CLP(ℤ), CLP(ℚ), CLP(ℝ)): Prolog's #= |
'=='(A, B): structural identity in the standard order of terms |
!= |
arithmetic disequality constraint: #\= |
(ISO spells it '=\\=' for arithmetic, '\\==' structurally) |
< > <= >= |
arithmetic ordering constraints: #< #> #=< #>=; ground dates, times and strings order too |
'<', '>', '=<', '>=': ISO arithmetic comparison, both sides evaluated (ISO spells =<, never <=) |
is |
unification, no evaluation: X is 1 + 2 binds X to the term 1 + 2 |
'is'(X, E): ISO is/2, evaluates E |
= |
not a goal (Python assignment is a syntax error in a clause body) | '='(A, B): unification |
In Clausal Prolog the bare spellings have the ISO meaning of the right-hand
column: == is structural identity, is evaluates, = unifies, and the
CLP(ℤ) constraints are #=, #<, ... from library(clpz).
A non-arithmetic term in an arithmetic constraint raises Scryer's clpz error
domain_error(clpz_expression, T): X == foo(1) raises
error(domain_error(clpz_expression, foo(1)), (==)/2).
Writing a quoted arithmetic cell¶
The functors of the evaluable table (+ - * / // div mod ** ^ rdiv rem,
unary - and +, abs min max sign gcd, the rounding functors truncate
round ceiling floor, float float_integer_part float_fractional_part,
sqrt sin cos tan asin acos atan atan2 exp log, the bitwise >> << /\ \/ \
xor, and the constants pi and e; see Arithmetic) are
builtins: they are in scope in every module, strict or not, with no
declaration, so rdiv(7, 2), '//'(A, B) and '^'(2, 3) are written as they
are in Prolog. As data, in a fact or a clause head, they are ordinary terms
(f(rdiv(1, 2)) holds the term rdiv(1, 2)); they are evaluated only where
arithmetic is ('is', eval_, a comparison, a constraint). A module's own
declaration of the same spelling answers first, with its usual arity checks.
The constants pi and e are atoms, so they need no declaration only in
arithmetic position ('is'(X, pi), eval_(2 * e, X), a comparison
operand, an evaluable functor's argument); as data (f(e), T is e) they
are ordinary atoms that a module declares like any other. Evaluating a bound
atom reads it as ISO does: T is e, 'is'(X, T) gives X = 2.718....
half_toward_zero(N, H) <- 'is'(H, '//'(N, 2))
test("toward zero") <- half_toward_zero(-7, -3)
test("bare floors") <- (eval_(-7 // 2, X), X == -4)
See Arithmetic for the evaluable table and eval_/2.