Syntax Module Users' Manual

Purpose

This document provides information on using the predicates exported from the syntax module. It assumes the user has a strong grasp of Prolog logic programming, including using operators defined by the op/3 directive and Definite Clause Grammars (DCG).

The syntax module provides the primitive logical connectives defined in Principia Mathematica,1 and, as such, provides the user a facility to translate mathematical proofs into executable code simply and correctly. Users familiar with (or practitioners of) Mathematical Philosophy2 should feel right at home with the operators exported from the syntax module.

Practically speaking, the syntax module provides fundamental consequence connectives used by the set_utils module and other modules. Its operators are similar to ones provided by other logic framework programming languages.3

:- module(syntax)

Exported Predicates

This module exports no predicates.

Exported Operators

The syntax module exports the following operators (n.b.: the using module must (re)declare the operator syntax):

sinon/[2,4]


Synopsis: +GoalA sinon +GoalB [MOD]
Arguments:  
GoalA<goal>a goal
GoalB<goal>a goal
Declaration: :- op(1100, xfy, sinon).
Description: 

sinon/[2,4] permits two proofs to be essayed consectively -- it's very much in the syntactic/semantic vein of ';'/[2,4], except that the order is deterministic for sinon/[2,4] (fr: 'if not') whereas 'GoalA; GoalB'/[2,4]'s order is not by nature fixed (I've observed situations where GoalB, usually the more general case, being executed first, leading to unintented results). This predicate is short-circuiting: if GoalA is true, GoalB is not pursued.

Truth table:  
 GoalA
 
GoalB
 truefail
true true true
fail true fail
Backtracking: 

Much like ';'/[2,4]. More specifically, all proofs for GoalA are exhausted first before attempting GoalB

Examples: 

This operator has proved to be useful for dealing with unexpected values:

seven_foot_bunch(Dayo, Day) sinon Day = unknown_day_of_week.

It can be used to assign a default as well:

hov_lane_aux(Code, Indicator) sinon Indicator = regular_lane.

Or, and this is a common pattern for throughout various systems, log a troublesome (but not terminal) event:

(Rest = []
     sinon log(5, "WARNING: ~s ~s ~s: '~s'",
	                          ["could not process the",
				   "following characters in",
				   "the input string", Rest])).
See Also: ';'/[2,4]
try/[1,3]

try/[1,3]


Synopsis: try +Goal [MOD]
Arguments:  
Goal<goal>a goal
Declaration: :- op(200, fx, try).
Description: 

try/[1,3] is really a derivative sinon/[2,4]: (try X) = (X sinon true). 'try Goal' calls Goal and simply succeeds, even if goal fails, preventing (some would say: 'subverting') bactracking over an establish proof branch.

Truth table:  
Goal
truefail
true true
Backtracking: 

All proofs for Goal are attempted

Examples: 

This predicate is excellent for protecting an established state from a predicate one wishes to essay but not have backtrack if it fails:

try score(Parties).
See Also: sinon/[2,4]

~/1


Synopsis: ~ +Goal (pron:"not" Goal) [MOD]
Arguments:  
Goal<goal>a goal
Declaration: :- op(900, fy, ~).
Description: 

Ever try to explain why \+/1 ('for every') means 'not' and not look stupid while 'explaining' the concept? It's very, very hard, especially given the somewhat different meaning of \+ in mathematics ... so, as a face-saving measure I propose the following reasonable alternative: ~/1.

Would a DCG version of '~Goal' make any sense?

Truth table:  
Goal
truefail
fail true
Backtracking: 

As for not/1 and \+/1.

Examples: 

The below example is true if S0 and S1 do not unify:

~ (S0 = S1)

Besides the standard usage (negation or complement), ~/1 can be doubled particularly to see if two things can be unified without changing the modality (this prevents free variables from being unintentionally bound):

try ({ ~ ~ Address <- Addresses }, list_utils:prepend(Driver)),

The above code prepends the Driver to the DCG if Address is in Addresses. If Address is a free variable, it remains free, or if there is a free variable in Addresses, that free variable in Addresses is also unchanged.

See Also: not/1
\+/1

/\/2


Synopsis: +GoalA /\ +GoalB (pron: GoalA "and" GoalB) [MOD]
Arguments:  
GoalA<goal>a goal
GoalB<goal>a goal
Declaration: :- op(500, yfx, /\).
Description: 

A semantic equivalent to ','/2; convenient for expressing mathematical proofs.

Truth table:  
 GoalA
 
GoalB
 truefail
true true fail
fail fail fail
Backtracking: 

As for ','/2.

Examples: 

A common pattern in the unit test framework is to test that a predicate gives a true (i.e. smaller) subset of the input data:

(X <- Actual => X <- Sales) /\ ~(X <- Actual <= X <- Sales).

The above statement asserts that Actual is smaller than Sales. In plain language: "All the elements of Actual are in Sales, AND there exists an X such that it is in Sales but not in Actual". Literally: "X is in Actual implies X is in Sales, AND not X is in Actuals is implied by X is in Sales."

See Also:','/2

\//2


Synopsis: +GoalA \/ +GoalB (pron: GoalA "or" GoalB) [MOD]
Arguments:  
GoalA<goal>a goal
GoalB<goal>a goal
Declaration: :- op(500, yfx, \/).
Description: 

A semantic equivalent to ';'/2; convenient for expressing mathematical proofs.

Truth table:  
 GoalA
 
GoalB
 truefail
true true true
fail true fail
Backtracking: 

As for ';'/2.

Examples: 

Currently only the syntax module itself uses \//2; a derivative example is:

(X = 2) \/ (X = 7)
See Also: ';'/2

<-/2


Synopsis: ?Element <- ?List (pron: Element "is in" List)
Arguments:  
Element<any>an atom or (list)term
GoalB<list>a list
Declaration: :- op(200, yfx, <-).
Description: 

A semantic equivalent to basics:member/2 that follows mathematic convention.

Backtracking: 

As for basics:member/2.

Examples: 

Many rule findings reduce to confirming the known information contains a particular type:

referred(Customer) <- Sales

But, of course, <- provides a convenient way of extracting information buried in context:

dob_from(Drivers, Name, DOB) :- driver(Name, dob(DOB, _), _) <- Drivers.
See Also:basics:member/2

=>/2


Synopsis: +GoalA => +GoalB (pron: GoalA "implies" GoalB) [MOD]
Arguments:  
GoalA<goal>a goal
GoalB<goal>a goal
Declaration: :- op(920, xfy, =>).
Description: 

Implication. Usually, in propositional logic one uses implication to relate a body of conjuctive clauses to a consequence. This operator is provided if it is not necessary to define a rule (in this case, one simply wishes to use implication "anonymously").

N.B.:

Implication is lexically-scoped: free variables bound within the statement remain bound only within the context of the implication. After the implication goes out of scope, the variable returns to its unbound state!

e.g.:
write2(A, B) :- format("~w ~w~n", [A, B]).
wild :-
  write2(Crazy, Eights),
  ((Crazy = Eights, Eights = 8) => write2(Crazy, Eights)),
  write2(Crazy, Eights).

wild/0 will print the representation of two free variables (e.g. '_6936 _6934'), then print two '8's and then print those two free, unrelated, variables, again (i.e., in this case, '_6936 _6934'), as 'Crazy' and 'Eights' have returned to their unbound, disassociated, states.

Truth table:  
 GoalA
 
GoalB
 truefail
true true true
fail fail true
Backtracking: 

As for ':-'/2.

Examples: 

=>/2 provides a compact way to embed mini-rule-like assertions into rule findings (without needing to take the extra step of defining these assertions as rules explicitly):

(miles(Distance) <- GreaterDistances => MinDistance =< Distance)

We are saying that "if we can find Distant in the GreateDistances list, then MinDistance must be less than or equal to that Distance" or "All 'Distance's in GreaterDistances are greater than MinDistance".

See Also: '<='/2
'<=>'/2
':-'/2

<=/2


Synopsis: +GoalA <= +GoalB (pron: GoalA "is implied by" GoalB) [MOD]
Arguments:  
GoalA<goal>a goal
GoalB<goal>a goal
Declaration: :- op(920, xfy, <=).
Description: 

Reverse implication. Actually, this operator, as opposed to =>, bears the closest resemblance to ':-'/2. As such, there are many practical applications of this operator when developing rules for (e.g.) expert systems.

N.B.:

This operator is short-circuiting: if GoalB fails, even though the result is "true", GoalA is no longer attempted.

e.g.:

A simple example of the short-circuiting behavior of the reverse implication operator is as follows:

write(hi) <= 1 = 2

The above statement is true, but the atom 'hi' is not printed.4

N.B.:

Reverse implication is lexically-scoped: free variables bound within the statement remain bound only within the context of the reverse implication. After the reverse implication goes out of scope, the variable returns to its unbound state!

e.g.:
lex_me(Out) :- (write(Out) <= Out = 2), Out = 5.

Given that lex_me/1 is called with a free variable, the above code will write '2' and then bind the variable (which is now free again) to 5.

N.B.:

The above example predicate (lex_me/1) demonstrates another facet of <=/2, which is that GoalB is proved first (that is, before GoalA). In pure logic programming (or, in the pure predicate calculus, instead of some other logic programming model, particularly temporal logics), this is an insignificant implementation detail, but, code relying on extralogical features (particular code relying on side effects, e.g. assert/1) may encounter unexpected results.

Truth table:  
 GoalA
 
GoalB
 truefail
true true fail
fail true true
Backtracking: 

As for ':-'/2.

Examples: 

The unit test framework to validate a proper subset relation between two sets:

X <- Actual => X <- CLOB /\ ~(Y <- Actual <= Y <- CLOB).

The above example ensures 'Actual' is a subset and smaller than 'CLOB'.

See Also: '=>'/2
'<=>'/2
':-'/2

<=>/2


Synopsis: +GoalA <=> +GoalB (pron: GoalA "if, and only if," GoalB, or GoalA "implies" GoalB, "and vice versa") [MOD]
Arguments:  
GoalA<goal>a goal
GoalB<goal>a goal
Declaration: :- op(920, xfy, <=>).
Description: 

Double implication, or mutually-dependent implication. The following pattern ...

A => B /\ A <= B

... in logic programming is so common that it has been replaced with the equivalent statement:

A <=> B
Truth table:  
 GoalA
 
GoalB
 truefail
true true fail
fail fail true
Backtracking: 

Actually, backtracking for <=>/2 is a subtle and beautiful thing,6 not only are all the possibilities explored where both GoalA and GoalB are true, but 'GoalA <=> GoalB' is also true where both GoalA and GoalB fail. This can be a rather frightening prospect to consider, but this issue is mitigated in that this op is most often used in the very restricted domain of comparing two fully-instantiated and finite sets. In this domain, then, backtracking through <=>/2 is not a matter of concern.

Examples: 

Expert systems sometimes operate on the principle of accumulating rule findings to reach a conclusion. One could write a unit test that ensures the new information is added when findings occur:

{ X <- PostAdd <=> X <- [RuleId|PreAdd] }.

More complex examples present themselves in the set_utils module, which has a foundation in the syntax of the predicate calculus implemented in this module.


Endnotes

1 More specifically, the logical connectives that form the miminal, useful, set of logical operators use by Gödel in "Über formal unentscheidbare Sätze der Principia Mathematica und verwandter Systeme" (trans: "On Formally Undecidable Propositions of Principia Mathematica and Related Systems"). See e.g.: [NNH2001], p. 70, table 2.
2 Bertrand Russell laid out the work of a mathematical philosopher in [Rus1919]; a wonderful little book that serves as a prologue to the Principia Mathematica.
3 The Mercury programming language provided the precedences for the operators defined here. See [Merc], Syntax chapter, § Builtin Operators.
4

One could amuse oneself with idle speculation: did the person who implemented the infix if/2 statement made so popular in Perl attempt to introduce a modicum of logic into the mess that is imperative programming, e.g.:

LINE: while(<STDIN>) {
    last LINE if /^From: /
}5

but that would be quite a stretch, and, besides, one has other things to do, like write more documentation, than to engage in such frivolity. Ah, well!

5

[SOC1997], p. 113.

6

As is backtracking in general, it is just that backtracking for <=>/2 is subtle and beautiful to a greater extent.

Works Consulted

[Merc] Mercury Language Reference Manual, Version 0.11.0, Fergus Henderson, Thomas Conway, Zoltan Somogyi, David Jeffery, Peter Schachte, Simon Taylor, Chris Speirs, Tyson Dowd, Ralph Becket, University of Melbourne (Australia, not Florida), http://www.cs.mu.oz.au/research/mercury. No date of publication given.
[NNH2001] Gödel's Proof (Revised Edition), Ernest Nagel and James R. Newman, edited and with a new foreward by Douglas R. Hofstadter, New York University Press, NY, 2001.
[Rus1919] Introduction to Mathematical Philosophy, Bertrand Russell, Dover Publications, Inc., NY, 1993.
[SOC1997] Learning Perl on Win32 Systems, Randal L. Schwartz, Erik Olson, Tom Christiansen, O'Reilly and Associates, Inc., Sebastopol, CA. 1997.

author:Douglas M. Auclair (dauclair at hotmail dot com)
date:December 15, 2005
Copyright © 2005, Cotillion Group, Inc. All rights reserved.