Qi Prolog is a version of Prolog that
provides the object code for the sequent calculus rules
of Qi. Unlike commercial stand-alone Prologs, Qi Prolog
is not inspired by the Warren Abstract Machine (WAM), but
instead by the AUM (Abstract Unification Machine) that is
biased towards producing efficient functional programming
object code. Under SBCL, Qi Prolog delivers about 300
KLIPS. Qi Prolog comes in two flavours - M Prolog based
on Edinburgh syntax and S Prolog based on Qi list
Qi Prolog M syntax is largely Edinburgh syntax for
reasons of familiarity with the standard Prolog
conventions. Since Qi uses a different reader to consult
a Prolog file from that used to load a Qi file, if you
wish to mix Edinburgh syntax (M-syntax) Prolog code with
Qi code in one file then the M-syntax Prolog has to be
placed in a sanitary string.
"member(X,[X | _]).
member(X,[_ | Y]) :- member(X,Y).")
Qi Prolog generates a function called member from the
previous definition. The function prolog?
receives any number of Prolog goals and returns true or
(12-) (prolog? (member 1 ) (member 2 [X]))
contains 8 extralogical predicates.
which is a binary predicate that corresponds to
the Edinburgh Prolog is. It can be written either
in prefix or infix form. So 'X is 1' and
'is(X,1)' are legal. The second expression is an
evaluable Qi expression. Thus 'X is (+ 3 4)' will
bind X to 7.
which works as a prefix is; bind is faster
however since the variables in the second
expression are not dereferenced.
which is a unary predicate that receives an
evaluable Qi expression e of type boolean. If e
is true then the call succeeds. If e is false
then it fails. If e is not a boolean then an
error is returned.
which works as when; fwhen is faster since the
variables in the second expression are not
is a unary predicate which receives an evaluable
Qi expression e. The Prolog computation is
stopped and e is returned as a result.
is a zero place predicate that performs the
is a unary predicate which receives a literal as
a list and applies the predicate of the literal
to its terms.
which enables multiple solutions to be found.
Here is len
(length) defined in Qi Prolog.
"len([ ], 0).
len([X | Y], N) :- len(Y,M), N is (+ M 1).")
Here return is used to coerce prolog? into returning a
(14-) (prolog? (len [a b c] N) (return N))
when and call can be
used to implement negation as failure (written here as
~). We then enquire if a and b are not unifiable
"~(P) :- call(P), !, fail. ~(P).
fail :- when(false).")
(prolog? (~ [= a b]))
And here when is used to define a
predicate that compares numbers.
(m-prolog "greater(X,Y) :- when((> X
As in Edinburgh Prolog, = performs unification between
terms. =! performs unification with an occurs-check. ==
compares two terms for strict equality.
(16-) (prolog? (= X 1))
(prolog? (= X [f X]))
(prolog? (=! X [f X]))
(prolog? (== X 1))
unify, unify! and identical
are legitimate substitutions for these predicates.
Note that repeated variables in Qi Prolog programs are
interpreted using occurs-check unification (in most
implementations of Prolog this is not true). Thus
(m-prolog "f(X, X).") is compiled exactly as
(m-prolog "f(X,Y) :- =!(X,Y)."). The function
occurs-check will change this behaviour. (occurs-check -)
disables automatic compilation with occurs-check
unification. (occurs-check +) restores the default. This
declaration also extends to the compilation of data types
and rule closures which compile into Qi Prolog.
Multiple Solutions in Qi Prolog
findall is a tertiary predicate that receives an input
pattern P, a literal L expressed as a list and an output
variable V. The variable V is bound to all those
instances of P for which the literal L is provable. This
is a generalisation of the usual Prolog findall
predicate. An example.
"app(, X, X).
app([X | Y], W, [X | Z]) :- app(Y, W, Z).")
(prolog? (findall [X Y] [app X Y [1 2 3]] Z) (return Z))
[[[ ] [1 2 3]] [ [2 3]] [[1 2] ] [[1 2 3] [ ]]]
It is easy to integrate this feature into a Qi function.
X -> (prolog? (findall Y [friend X Y] Z) (return Z)))
A mode declaration in Qi Prolog is used to suspend
unification in favour of pattern-matching and is used
only in the head of a clause. Generally mode declarations
are used to speed up code and reduce the footprint of the
generated Lisp code. (mode .. -) enables
pattern-matching. Here is an example.
(22-) (m-prolog "f(a).")
(prolog? (f X))
(m-prolog "f((mode a -))")
(prolog? (f X))
(prolog? (f a))
The default mode is unification signalled by (mode
+). Mode declarations can be nested within each other as
in (mode [f a (mode b +)] -). In this event the innermost
declaration has priority. The above expression would thus
perform pattern-matching with respect to [f a b] and all
its elements save b which would receive unification.
of Applications in Qi Prolog
Qi Prolog allows function expressions to be embedded into
the tails of Horn clauses. Thus
f(X) :- g((* 2 X)).
would create a Horn clause procedure that receives an
input and doubles it, passing the result to g. The
) notation can be used in the head of a Horn
clause but its significance is different.
signifies a predicate that expects to receive a list
structure [1 2 3]. The (
) construction is actually
syntactic sugar for a list construction containing a
complex series of mode declarations.
f((1,2,3)). is equivalent to f([1 | (mode [(mode 2 +) |
[(mode 3 +) | [ ]]]] -)). The expression (1,2,3) is a
complex term and the mode declarations effectively mean
that (1,2,3) is either unified to a variable or unified
element by element to the corresponding term. Hence a
complex term cannot be broken up within unification in
the way that a simple list can be.
Here is an illustration.
(1-) (m-prolog "try((f a b)).")
(prolog? (try X) (return X))
[f a b]
(prolog? (try [A B C]) (return A))
(prolog? (try [A | B]) (return A))
The final input fails because a complex term cannot be
broken up as a list can. In general complex terms process
faster than lists and take less code space because of
their mode declarations. They are useful in automated
reasoning applications where terms are to be treated as
logical units rather than decomposable lists.
The S syntax version of Qi Prolog is more suitable than
the M syntax version for the machine generation of Horn
clauses. In the internals of the M Prolog compiler, M
Prolog is converted first into S Prolog, before being
compiled to Lisp. s-prolog receives a list of Horn
clauses expressed in S syntax format and compiles them to
A Horn clause in S syntax has the form [Head :- Body]
where Head is a literal and Body is a (possibly empty)
list of literals. A literal is a list headed by a symbol
(the predicate) and followed by a series of n (n ³ 0)
terms which may themselves be lists. The easiest way to
understand S syntax is to compare M syntax clauses with
their S syntax equivalents.
a] :- [ ]]])
"m(X,[X | _]).
m(X,[_ | Y]) :- m(X,Y).")
[[[m X [X | _]] :- [ ]]
[[m X [_ | Y]] :- [[m X Y]]]])
len([_ | Y],N) :- len(Y,M), N is (+ M 1).")
[[[len [ ] 0] :- [ ]]
[[len [_ | Y] N] :- [[len Y M] [is N [+ M 1]]]])
"c(X,Y) :- ==(X,Y).
d(X,Y) :- =(X,Y).
e(X,Y) :- =!(X,Y).")
[[[c X Y] :- [[== X Y]]]
[[d X Y] :- [[= X Y]]]
[[e X Y] :- [[=! X Y]]]])
Copyright (c) 2008, Mark