Paul TarauDepartement d'Informatique Universite de Moncton, Moncton, Canada, E1A 3E9, tarau@info.umoncton.ca, September 7, 1995
CONTINUATIONS
as FIRST
ORDER
OBJECTS
BACKTRACKBLE
DESTRUCTIVE ASSIGNMENT
EITHER: Edit `Makefile'. Change BINDIR and comment out ARCH_AND_OS, for example: BINDIR = /usr/local/bin ARCH_AND_OS = sparc.sunos then TYPE: make install OR: Copy or symbolically link bin/bp. to `bp' somewhere in your path, then type `bp'.
$ bp command-line options wam-bytecode-file
$ bp
usage: [-h heap] [-s stack] [-t trail] [-c code] [-b bboard] [-a 2^atoms] [-d 2^hashtable entries] [-i size of IO-buffer] [-q quietness] wamfile (default: wam.bp).
ftp clement.info.umoncton.ca (139.103.16.2)in the directory:
/pub/BinProlog.The ftp server clement.info.umoncton.ca is a Sparcstation ELC with SUNOS 4.1.1). Please send comments and bug reports to binprolog@info.umoncton.ca.
BINARIZATION
BinProlog is a small, program-transformation based compiler. Everything is
converted to Continuation Passing Binary Clauses:
A rule like
a(X):-b(X),c(X,Y),d(Y).becomes
a(X,Cont):-b(X,c(X,Y,d(Y,Cont))).A fact like
a(13).becomes
a(13,Cont):-true(Cont).A predicate using metavariables like
p(X,Y):-X,Y.becomes
p(X,Y,Cont);-call(X,call(Y,Cont))).with true/1 and call/2 efficient builtins in BinProlog.
MACHINES
SUPPORTED
This distribution contains the Prolog source of the compiler and
executable emulators for:
- Sparc - SunOS 4.x, Solaris 2.x; - DEC Alpha - 64 bit - 68k NeXT - Mach; SUN3 - SunOS 4.x - MIPS - DEC and SGI; - IBM R6000; - 386-486-Pentium (MsDOS+Windows - with 32bits DOS-extender go32 ver. 1.10, Linux, FreeBSD).As the implementation makes no assumption about machine word size it is likely to compile even on very strange machines that have a C-compiler. BinProlog's integers are inherited from the native C-system. For example on DEC ALPHA machines BinProlog has 64 - 3 = 61 bit integers. On 32-bit systems it has 32 - 2 = 30 bit integers. Floating point is double (64 bits) and it is guaranteed that computations in Prolog will always give the same results as in the underlying C. As a matter of fact BinProlog does not really know that it has floats but how this happens is rather long to explain here.
?-bb_def(likes,joe,[any(beer),good(vine),vegetarian(food)]).
?-bb_set(likes,joe,nothing).
?-bb_rm(likes,joe).
?-bb_val(likes,joe,What).
?- Person=joe, friend#Person:=:mary, bb.
?- friend # joe:=:X.
?- for(I,1,99),bb_def(table,I,f(I,I)),fail. ?- bb.
?- X is cos(3.14)+sin(0).
?- cos(1,X).
THE INTERACTIVE
TOPLEVELSHELL
To see the command line options:
$ bp -helpTo compile and load file or file.pl or file.pro
?-[file].BinProlog searches them in the directories ., ./progs and ../src ../library, relative to the BP_PATH environment variable which defaults silently to the current directory if undefined. The dirctory ./myprogs is also searched for possible user programs. While compatible to previous configurations which where single user minded, this allows installing the BinProlog distribution for shared use in a place like /usr/local/BinProlog(4)
USING MULTIPLE
LOGIC ENGINES
A new (experimental) feature added starting with version 3.39 allows launching
multiple Prolog engines having their own stack groups (heap, local stack
and trail). An engine can be seen as an abstract data-type which
produces a (possibly infinite) stream of solutions as needed.
To create an engine use:
% :-mode create_engine(+,+,+,-). create_engine(HeapSize,StackSize,TrailSize,Handle)
% :-mode load_engine(+,+,+). load_engine(Handle,Goal,AnswerVariable)
% :-mode ask_engine(+,-). ask_engine(Handle,Answer)
% :-mode destroy_engine(+). destroy_engine(Handle)to free the data areas allocated for the engine. If you want to submit another query before using the complete stream of answers, it is more efficient to reuse an existing engine with load_engine/3, instead of destroying it and creating a new one.
?-create_engine(256,64,64,E), load_engine(E,append(As,Bs,[A,B,B,A]),As+Bs), ask_engine(E,R1),write(R1),nl, ask_engine(E,R2),write(R2),nl, load_engine(E,member(X,[1,2,3]),X), ask_engine(E,R3),write(R3),nl, ask_engine(E,R4),write(R4),nl, destroy_engine(E).
FINDALL/3
with MULTIPLE
ENGINES
The file library/engines.pl contains som examples
of how multiple engines can be used for implementing
for instance all-solution predicates. Here is a re-entrant
find_all/3.
find_all(X,Goal,Xs):- default_engine_params(H,S,T), find_all(H,S,T,X,Goal,Xs). find_all(H,S,T,X,Goal,Xs):- create_engine(H,S,T,Handle), load_engine(Handle,Goal,X), collect_answers(Handle,Xs), destroy_engine(Handle). collect_answers(Handle,[X|Xs]):-ask_engine(Handle,A),!, copy_term(A,X), collect_answers(Handle,Xs). collect_answers(_,[]). default_engine_params(128,32,32).
COMMUNICATION THROUGH
LINEAR ASSUMPTIONS
A more interesting application is based on the synergy between
intuitionistic and linear implication and their continuation
passing counterparts assumei/1 and assume/0 and multiple
engines.
% executes Producer on an `orthogonal' OR-engine Answer<=Producer=>Consumer:-!, % the consumer is explicit cross_run(Producer,Answer,Consumer). Answer<=Producer:- % the consumer is in the `future' cross_run_with_current_continuation(Producer,Answer). cross_run(Producer,Answer,Consumer):- default_engine_params(H,S,T), cross_run(H,S,T,Producer,Answer,Consumer). cross_run(H,S,T,Producer,Answer,Consumer):- compound(Answer),copy_term(Answer,A), new_engine(H,S,T,Producer,Answer,Handle), (A:-ask_or_kill(Handle,A))=>Consumer, destroy_engine(Handle). cross_run_with_current_continuation(Producer,Answer):- default_engine_params(H,S,T), cross_run_with_current_continuation(H,S,T,Producer,Answer). cross_run_with_current_continuation(H,S,T,Producer,Answer):- compound(Answer),copy_term(Answer,A), new_engine(H,S,T,Producer,Answer,Handle), assumei((A:-ask_or_kill(Handle,A))). ask_or_kill(Handle,Answer):-ask_engine(Handle,Answer),!. ask_or_kill(Handle,_):-destroy_engine(Handle),fail. new_engine(H,S,T,Goal,Answer,Handle):- create_engine(H,S,T,Handle), load_engine(Handle,Goal,Answer). % overridable default engine sizes for heap,stack,trail default_engine_params(H,S,T):-assumed(engine_params(H,S,T)),!. default_engine_params(128,32,32).
?- a(X)<=member(X,[1,2,3])=>(a(A),a(B),a(C),write([A,B,C]),nl). [1,2,3]
REAL STANDALONE
APPLICATIONS through
COMPILATION
to C
Starting with version 3.30 it is possible to separately compile user
applications and just link them with the emulator library and the
C-ified compiler (see directory pl2c,). This allows creation of
a fully C-ified application in a few seconds.
UNIX
PSEUDO-EXECUTABLES
The following still works, although the new C-ification technique
(see directories pl2c, dynpl2c) can now create true one-file standalones.
It is possible to create (as in the past)
a small runtime application (called "newappl.bp"), from
?-make_appl('Deliver the appropriate "bp" file (the Prolog engine) with your application. The user must type:.pl').
$ bp newappl.bpto start it.
Note: the file must contain a clause
main(X):-...That's where execution starts. BinProlog's shell is nothing but such an application which starts with a toplevel loop i.e. something like:
main(X):-toplevel(X).As a new feature, you can override this behaviour, even for the standard BinProlog system containing the predicate main/1 (which starts the interactive top-level), by defining a predicate main/0 in your program file. In this case, main/0 becomes the new starting point.
% FILE: hello.pl main(true):-write(hello),nl. % DO: % ?-make_executable_unix_appl( % '/usr/local/bin/bp -q5', % AbsolutePathToEmulator (q=quietness) % 'hello.pl', % SourceFile % hello). % ExecutableFile
$ helloto start it.
The code of this primitive is at the end of the file co.pl.
You can bootstrap the compiler, after modifying the *.pl files by:
?- boot. ?- make.
?-make(ProjectFile). ?-make(ProjectFile,Module).
?-cmake(ProjectFile). ?-cmake(ProjectFile,Module).
This allows to integrate your preferences and extensions written in Prolog to the BinProlog kernel.
Make sure you keep a copy the original
wam.bp in a safe place, in case things go wrong when you type ?-boot.
SOME
LIMITATIONS/FEATURES
of
BINPROLOG
We passed Occam's razor on a few "features" of Prolog that are
obsolete in modern programming environments and on some others that are,
in our opinion, bad software practice.
?-[myfile].
?- compile(myfile).
:-[include1]. :-[include2]. ....
Programs that work well can be added to the BinProlog kernel. This avoids repeated recompilation of working code and predicates in the kernel are protected against name clashing.
?- compile(wam,[file1,file2,...],'application.bp').
Here are some other limitations/features:
?- interactive(no).or turn it on again with
?- interactive(yes).
OTHER
BINPROLOG
GOODIES and
NEW PREDICATES
A few BinProlog specific predicates are available:
EFFICIENT
FINDALL BASED
META-PROGRAMMING
BinProlog's findall/3 is so efficient that you can
afford (with some care) to use it instead of explicit
(and more painful) first-order programs as in:
% maplist with findall maplist(Closure,Is,Os):- Closure=..L1, det_append(L1,[I,O],L2), P=..L2, findall(O,map1(P,I,Is),Os). map1(P,I,Is):-member(I,Is),P.
?- maplist(+(1),[10,20,30],T). => T=[11,21,31]
BUILTINS
The following (user-level) builtins are supported, with semantics
(intended to be) close to SICSTUS and QUINTUS Prolog.
fail/0 nl/0 var/1 nonvar/1 integer/1 atomic/1 +/3 % arithmetic offers also the usual is/2 interface -/3 * /3 // /3 mod/3 << /3 >> /3 /\ /3 \/ /3 # /3 % bitwise XOR \ /3 % bitwise NOT random/1 % returns an integer, not a float get0/1 put/1 < /2 > /2 =< /2 >= /2 =:= /2 =\= /2 compare/3 seeing/1 seen/0 telling/1 told/0 copy_term/2 functor/3 arg/3 name/2 abort/0 is_compiled/1 % checks if a predicate is compiledOther useful predicates are defined in the file lib.pl and work as expected.
true/0, =/2, .. -> .. ; .. ;/1 call/1 \+/1 repeat/0 findall/3 findall/4 for/3 numbervars/4 see/1 tell/1 system/1 statistics/0 statistics/2 atom/1 compound/1 =../2 length/2 tab/1 get/1 == /2 \== /2 A @< B A @> B A @=< B A @>= B compile/1 - see co.pl for the compiler read/1 - see the file: read.pl write/1 - see the file: write.pl halt/0, halt(ReturnCode), listing/0, % only for interpreted code listing(Pred,Arity), assert/1, asserta/1, assertz/1, retract/1, retractall/1 erase/1, dynamic/1, instance/2, clause/2, clause/3, consult/1, % usable for debugging or dynamic code reconsult/1, % they override compiled definitions !!! setof/3, bagof/3, sort/3, keysort/3, setarg/3, % backtrackable update of arg I of term T with new term X is_builtin/1 % lists the system-level builtins (written in C): current_predicate/1 % lists/checks existence of a predicate/arity predicate_property/2 % lists/checks if a property (arg 2) is associated with a head (arg 1)Operators are defined and retrieved with
:-op/3, current_op/3.
:-compile(file).
:-[file]. % this calls the compiler too !!!
:-consult(file).in your (unique) top-level file. This overcomes the limitation of having only one top-level file.
(module)/1 current_module/1 is_module/1 - checks/generates an existing module-name module_call/2, ':'/2 - calls a predicate hidden in a module module_name/3 - adds the name of a module to a symbol module_predicate/3 - adds the name of a module to a goal modules/1 - gives the list of existing modules
:-module m1. :-public d/1. a(1). a(2). a(3). a(4). d(X):-a(X). :-module m2. :-public b/1. b(X):-c(X). c(2). c(3). c(4). c(5). c(6). :-module m3. :-public test/1. test(X):-b(X),d(X). :-module user. go:-modules(Ms),write(Ms),nl,fail. go:-test(X),write(X),nl,fail.
[user/0,m1/0,m2/0,m3/0] 2 3 4
:-module(beings,[cat/4,dog/4,chicken/2,fish/0,maple/1,octopus/255]).
?- edit(, ).
?- ed.
?- edit.
?- co.The debugger/tracer uses R.A. O'Keefe's public domain meta-interpreter. You can modify it in the file "extra.pl".
DCG-expansion is supported by the public domain file dcg.pl.
?- reconsult(FileName).and then
?- trace(Goal).For interactivity, both the toplevel and the debugger depend on
?-interactive(yes).or
?-interactive(no).My personal preference is using interactive(no) within a scrollable window. However, as traditionally all Prologs hassle the user after each answer BinProlog 4.00 will do the same by default.
?- [allperms]. compiling(to(mem),progs/allperms.pl,...) compile_time(230) ?- consult(user). % using compile/1 is MUCH faster reconsulting(user) g0(N):-nats(1,N,Ns),perm(Ns,_),fail. g0(_). WARNING: redefining compiled predicate(g0/1) perm([],[]). perm([X|Xs],Zs):- perm(Xs,Ys), insert(X,Ys,Zs). insert(X,Ys,[X|Ys]). insert(X,[Y|Ys],[Y|Zs]):- insert(X,Ys,Zs). WARNING: redefining compiled predicate(perm/2) WARNING: redefining compiled predicate(insert/3) reconsulted(user,time = 20) yes ?- interactive(no). yes ?- trace(g0(3)). Call: g0(3) Call: nats(1,3,_645) compiled(nats(1,3,_645)) Exit: nats(1,3,[1,2,3]) Call: perm([1,2,3],_648) Call: perm([2,3],_1095) Call: perm([3],_1362) Call: perm([],_1629) Exit: perm([],[]) Call: insert(3,[],_1362) Exit: insert(3,[],[3]) Exit: perm([3],[3]) Call: insert(2,[3],_1095) Exit: insert(2,[3],[2,3]) Exit: perm([2,3],[2,3]) Call: insert(1,[2,3],_648) Exit: insert(1,[2,3],[1,2,3]) Exit: perm([1,2,3],[1,2,3]) Call: fail compiled(fail) Fail: fail Redo: perm([1,2,3],[1,2,3]) Redo: insert(1,[2,3],[1,2,3]) Call: insert(1,[3],_2683) Exit: insert(1,[3],[1,3]) Exit: insert(1,[2,3],[2,1,3]) Exit: perm([1,2,3],[2,1,3]) Call: fail compiled(fail) Fail: fail Redo: perm([1,2,3],[2,1,3]) Redo: insert(1,[2,3],[2,1,3]) Redo: insert(1,[3],[1,3]) Call: insert(1,[],_2945) Exit: insert(1,[],[1]) Exit: insert(1,[3],[3,1]) Exit: insert(1,[2,3],[2,3,1]) Exit: perm([1,2,3],[2,3,1]) Call: fail compiled(fail) Fail: fail Redo: perm([1,2,3],[2,3,1]) Redo: insert(1,[2,3],[2,3,1]) Redo: insert(1,[3],[3,1]) Redo: insert(1,[],[1]) Fail: insert(1,[],_2945) Fail: insert(1,[3],_2683) Fail: insert(1,[2,3],_648) Redo: perm([2,3],[2,3]) Redo: insert(2,[3],[2,3]) Call: insert(2,[],_2421) Exit: insert(2,[],[2]) Exit: insert(2,[3],[3,2]) Exit: perm([2,3],[3,2]) Call: insert(1,[3,2],_648) Exit: insert(1,[3,2],[1,3,2]) Exit: perm([1,2,3],[1,3,2]) Call: fail compiled(fail) Fail: fail Redo: perm([1,2,3],[1,3,2]) Redo: insert(1,[3,2],[1,3,2]) Call: insert(1,[2],_2945) Exit: insert(1,[2],[1,2]) Exit: insert(1,[3,2],[3,1,2]) Exit: perm([1,2,3],[3,1,2]) Call: fail compiled(fail) Fail: fail Redo: perm([1,2,3],[3,1,2]) Redo: insert(1,[3,2],[3,1,2]) Redo: insert(1,[2],[1,2]) Call: insert(1,[],_3207) Exit: insert(1,[],[1]) Exit: insert(1,[2],[2,1]) Exit: insert(1,[3,2],[3,2,1]) Exit: perm([1,2,3],[3,2,1]) Call: fail compiled(fail) Fail: fail Redo: perm([1,2,3],[3,2,1]) Redo: insert(1,[3,2],[3,2,1]) Redo: insert(1,[2],[2,1]) Redo: insert(1,[],[1]) Fail: insert(1,[],_3207) Fail: insert(1,[2],_2945) Fail: insert(1,[3,2],_648) Redo: perm([2,3],[3,2]) Redo: insert(2,[3],[3,2]) Redo: insert(2,[],[2]) Fail: insert(2,[],_2421) Fail: insert(2,[3],_1095) Redo: perm([3],[3]) Redo: insert(3,[],[3]) Fail: insert(3,[],_1362) Redo: perm([],[]) Fail: perm([],_1629) Fail: perm([3],_1362) Fail: perm([2,3],_1095) Fail: perm([1,2,3],_648) Redo: nats(1,3,[1,2,3]) Fail: nats(1,3,_645) Exit: g0(3)
% FILE: jbond.pl :-spy a/1. :-spy c/1. b(X):-a(X),c(X). a(1). a(2). c(2). c(3).This gives the following interaction:
?-[jbond]. ...... ?- b(X). Call: a(_2158)Although these are very basic debugging facilities you can enhance them at your will and with some discipline in programming they may be all you really need. Anyway, future of debugging is definitely not by tracing. One thing is to have stronger static checking. In dynamic debugging the way go is to have a database of trace-events and then query it with high level tools. We plan to add some non-tracing database-oriented debugging facilities in the future.: ; !!! compiled(a/1) Exit: a(1) Call: c(1) : ; !!! compiled(c/1) Fail: c(1) Redo: a(1) Exit: a(2) Call: c(2) : ; !!! compiled(c/1) Exit: c(2) X=2; Redo: c(2) Fail: c(2) Redo: a(2) Fail: a(_2158) no
You can generate a kind of intermediate WAM-assembler by
?- compile(asm,[file1,file2,...],'huge_file.asm').A convenient way to see interactively the sequence of program transformations BinProlog is based on is:
?- asm. a-->b,c,d. DEFINITE: a(A,B) :- b(A,C), c(C,D), d(D,B). BINARY: a(A,B,C) :- b(A,D,c(D,E,d(E,B,C))). WAM-ASSEMBLER: clause_? a,3 firstarg_? _/0,6 put_structure d/3,var(4-4/11,1/2) write_variable put,var(5-5/10,1/2) write_value put,var(2-2/6,2/2) write_value put,var(3-3/7,2/2) put_structure c/3,var(3-8/14,1/2) write_variable put,var(2-9/13,1/2) write_value put,var(5-5/10,2/2) write_value put,var(4-4/11,2/2) execute_? b,3
:-set_c_threshold(Min,Max).
PERFORMANCE of
C-IFIED
CODE
The speed-up clearly depends on the amount of C-ification
and on the statistical importance of C-ified code in
the execution profile of a program (see figure 1).
We have noticed between 10-20% speed increase for
programs which take advantage of C-ified code moderately,
As these programs spend only 20-30% of their time
in C-ified sequences performances are expected
to scale correspondingly when we extend this approach to the full BinProlog
instruction set and implement low-level gcc direct jumps instead
of function calls and anti-calls.
Figure 1: Performance of emulated (emBP) and partially C-ified BinProlog 3.22
(C-BP) compared to emulated (emSP) and native (natSP) SICStus 2.1_9 on a Sparc
10/20).
chunks can influence adversely not only on
size but also on speed. Something like threshold=20,
looks like a practical optimum for this program.
or
NAMING
PRIMITIVES
The first four are simply an interface to BinProlog's unique global hashing table working as an
COPYING
PRIMITIVES
Copy_term/2 is Prolog's usual primitive extended to copy objects from the heap and also from blackboard to the current top of the heap. We refer to
[Tarau92:ECO] for the implementation and memory management aspects of
these primitives.
Save_term/2 copies an object possibly distributed over the heap and the blackboard to a new blackboard object. It also takes care not to copy parts of the object already on the blackboard.
BLACKBOARDN BASED
FAILURE SURVIVING
STACKS
A very useful data structure that can be implemented with the blackboard is
a stack that survives failure but still allows the programmer to use some of the nice properties of logical variables.
CONSTANT TIME
VECTORS
Defining a vector is done initializing it to a given Zero
element. The vector_set/3 update operation uses saved/2, therefore
the old content of vectors is also subject to garbage collection.
The following is a blackboard based complete knight-tour, adapted from
Evan Tick's well known benchmark program.
A LEMMA
BASEDTAK
The following tak/4 program uses lemmas to avoid heap explosion in the case of
of a particularly AND intensive program with 4 recursive calls, a problem particularly severe in the case of the continuation passing binarization that BinProlog uses to simplify the WAM. To encode the 2 first arguments in a unique integer some bit-shifting is needed as it can be seen in tak_encode/3. To avoid such problems, hashing on arbitrary terms like Quintus Prolog's term_hash/2 looks a very useful addition to BinProlog.
--> O (instantiated executing G)
tak_lemma(P,I,_,O):-val(P,I,X),!,X=O.
tak_lemma(P,I,G,O):-G,!,def(P,I,O).
go:- statistics(runtime,_),
tak(24,16,8,X),
statistics(runtime,[_,T]),statistics,
write([time=T,tak=X]), nl.
The reader interested in the internals of BinProlog and other
issues related to binary logic programs, their transformations
and performance evaluation is referred to
[Tarau90:PLILP],[Tarau91:JAP],
[Tarau91:RU],[Demoen90:KUL],
[Demoen91:RU], [Tarau92:WAMOpt],
[Tarau92:ECO],[LOPSTR93:Neumerkel],
[Neum92],
[Tarau93:GULP],
[Tarau93a],[kdb93f],
[WA83],
[lindgren], [TD94:WE],
[TA94:JFPL],
[TN94:PLILP],[kdb93j],[kdb93d]
Bmark/Compiler
emBP
C-BP
emSP
natSP
NREV (KLIPS)
445
455
412
882
CAL (ms)
490
310
590
310
FIBO (ms)
1730
1320
1400
800
TAK (ms)
610
470
400
180
SEMI3 (ms)
1810
1410
1810
1310
QUEENS (ms)
3170
2220
2840
1070
threshold: 0 4 8 20 30 1000 emBP emSP natSP
size: (K) 34.5 32.2 29.9 16.3 13.1 12.9 4.8 22.0 31.9
speed: (ms) 1480 1430 1440 1450 1810 1790 1800 1810 1310
THE
BLACKBOARD
Syntax: A#B:=:X, or lval(A,B,X).
where X is any term on the heap.
It has simply a global name A#B, i.e. an
entry in the hashing table with keys A and B. The address in the table
(C-pointers are the same as logical variables in BinProlog) is trailed
such that on backtracking it will be unbound (i.e. point to itself).
Unification with A#B:=:Y is possible at any point in the program which
knows the `name' A#B.
GARBAGE-COLLECTIBLE
PERMANENT OBJECTS.
On the other hand, if bb_def/3 or bb_set/3 is used to name objects on the
blackboard, they "survive" backtracking and can afterwards be retrieved as
logical variables using bb_val/3.
bb_def/3 (i,i,i) defines a value
bb_set/3 (i,i,i) updates a value
bb_rm/2 (i,i) removes a value
bb_val/3 (i,i,o) retrieves the value
ASSERT and
RETRACT
For compatibility reasons
BinProlog has them, implemented on top of the more efficient
blackboard manipulation builtins.
?-metacall(Goal).
instead of
?- Goal.
assert/1
asserta/1
assertz/1
retract/1
clause/2
metacall/1
abolish/2
ASSUMED CODE ,
INTUTIONISTIC and
LINEAR IMPLICATION
Intuitionistic assumei/1 adds temporarily a clause usable
in later proofs. Such a clause can be used an indefinite
number of times, mostly like asserted clauses, except that it
vanishes on backtracking. Its scoped version Clause=>Goal
or [File]=>Goal makes Clause or the set of clauses found in
File available only during the proof of Goal. Both vanish on backracking.
Clauses are usable an indefinite number of times in the proof, i.e.
for instance ?-assumei(a(13)),a(X),a(Y) will succed.
?-a(10)-:a(11)=>a(12)-:a(13)=>(a(X),a(X)).
X=11;
X=13;
no
OVERRIDING
Assumed predicates will override similarly named dynamic predicates
which in turn will override compiled ones. Note that overriding
is done at predicate, not clause level.
Note also that multifile compiled
clauses are still forbidden. However, multifile assumed and
dynamic code is now accepted.
BinProlog 4.00 (with C-source license allowing recompilation with
-D JUMP_COMPRESS=0 allows a stronger overriding
mechanisme. The new builtin override(OldPred,NewPred)
backtrackably overrites the header of the compiled definition
of OldPred to with a jump instruction to the definiton of
compiled predicate NewPred. The builtin also works with C-ified
code, which, usually benefits in speed from recompilation
with -DJUMP_COMPRESS=0-<>.
LANGUAGE
ENHANCEMENTS
BASED on
LINEAR IMPLICATION
Linear implication can be used to quickly add some language
enhancements to a Prolog system. For instance, to add
a switch ... case statement one can write simply:
switch(Selector,Body):-
case(Selector)-:Body.
test(X):-
switch(X, (
case(1)->write(one) ;
case(2)->write(two) ;
otherwise->write(unexpected(X))
))
,nl.
% ?-test(2),test(13).
PROBLEM
SOLVING with
LINEAR IMPLICATION
Linear implication is a serious and very convenient problem
solving tool, which allows avoiding explicit handling of
complex data-structures. Let's suppose we want to walk through
a (possibly circular) graph structure without looping.
path(X,X,[X]).
path(X,Z,[X|Xs]):-linked(X,Y),path(Y,Z,Xs).
linked(X,Y):-c(X,Ys),member(Y,Ys).
go(Xs):-
c(1,[2,3]) -: c(2,[1,4]) -: c(3,[1,5]) -: c(4,[1,5]) -:
path(1,5,Xs).
path(X,X,[X]).
path(X,Z,[X|Xs]):-c(X,Y),path(Y,Z,Xs).
% data
go(Xs):-
(c(1,X1):-c1(X1)) -:
(c(2,X2):-c2(X2)) -:
(c(3,X3):-c3(X3)) -:
(c(4,X4):-c4(X4)) -:
path(1,5,Xs).
c1(2).
c1(3).
c2(1).
c2(4).
Some finite domain constraint solving can also be done
quite efficiently (1.3 seconds on a Sparc 10-20 for
the SEND + MORE = MONEY puzzle - see file progs/lconstr.pl).
% Program: linear implication based FD constraint solving
% Author: Paul Tarau, 1995
% cryptarithmetic puzzle solver -see progs/lconstr.pl
% a kind of "constraint solving with linear implication"
example1(
[s,e,n,d,m,o,r,e,y]=[S,E,N,D,M,O,R,E,Y],
[S,E,N,D]+
[M,O,R,E]=
[M,O,N,E,Y],
_
).
% Addition of two numbers - simplified version -
sum(As, Bs, Cs) :- sum(As, Bs, 0, Cs).
sum([], [], Carry, [Carry]).
sum([A|As], [B|Bs], Carry, [C|Cs]) :- !,
add2digits(A,B,Carry,C,NewCarry),
sum(As, Bs, NewCarry, Cs).
add2digits(A,B,Carry,Result,NewCarry):-
bind(A),bind(B),
add_with_carry(10,A,B,Carry,Result,NewCarry).
add_with_carry(Base,A,B,Carry,Result,NewCarry):-
S is A+B+Carry,
Result is S mod Base,
NewCarry is S // Base,
new_digit(Result).
bind(A):-var(A),!,digit(A).
bind(_).
new_digit(A):-digit(A),!.
new_digit(_).
solve(As,Bs,Cs,Z):-
digit(0)-:digit(1)-:digit(2)-:digit(3)-:digit(4)-:digit(5)-:
digit(6)-:digit(7)-:digit(8)-:digit(9)-:
( sum(As,Bs,Cs),
Z>0
).
puzzle:-
init(Vars,Puzzle,Names),
Puzzle=(Xs+Ys=Zs),Zs=[Z|_],
reverse(Xs,As), % see progs/lconstr.pl
reverse(Ys,Bs),
reverse(Zs,Cs),
solve(As,Bs,Cs,Z),
show_answer(Vars,Names,Puzzle), % see progs/lconstr.pl
fail.
puzzle:-
write('no (more) answers'),nl,nl.
go:-
(init(X,Y,Z):-example1(X,Y,Z))-:puzzle,
fail.
Notice how linearly assumed digit/1 facts are consumed
by bind/1 and new_digit/1 to enforce constraints as early
as possible inside the addition loop.
THE BLACKBOARD
as an
ALTERNATIVE to
ASSERT and
RETRACT
Designed in the early stages of Prolog, assert and retract have been
overloaded with different and often conflicting requirements. They try
to be both the way to implement permanent data structures for global
state information, self-modifying code
and tools for Prolog program management. This created
not only well-known semantical but also expressivity and efficiency
problems.
THE
LOW-LEVEL
BLACKBOARD OPERATIONS
Six low-level(5)
primitives def/3, set/3, val/3, rm/2, copy_term/2, save_term/2 are
used in BinProlog 4.00
to fully replace assert and retract while keeping distinct their overloaded naming and copying function.
Atomic-key1,Atomic-key2-->HeapOrBlackboardObject.
function.
AN USEFUL
PROLOG EXTENSION:
BESTOF/3
Bestof/3 is missing in all current Prolog implementations we
know of. BinProlog's bestof/3 works like findall/3, but
instead of accumulating alternative solutions, it selects successively
the best one with respect to an arbitrary total order
relation. If the test succeeds the new answer replaces the previous
one. At the end, either the query has no answers, case in which bestof
fails, or an answer is found such that it is better than every
other answer with respect to the total order. The proposed syntax is
?-bestof(X,TotalOrder,Goal)
At the end, X is instantiated to the best answer.
For example, the maximum of a list of integers can be defined simply as:
max(Xs,X):-bestof(X,>,member(X,Xs)).
The following is an efficient implementation
implementation using the blackboard.
% true if X is an answer of Generator such that
% X Rel Y for every other answer Y of Generator
bestof(X,Closure,Generator):-
copy_term(X,Y),
Closure=..L1,
det_append(L1,[X,Y],L2),
Test=..L2,
bestof0(X,Y,Generator,Test).
bestof0(X,Y,Generator,Test):-
inc_level(bestof,Level),
Generator,
update_bestof(Level,X,Y,Test),
fail.
bestof0(X,_,_,_):-
dec_level(bestof,Level),
val(bestof,Level,X),
rm(bestof,Level).
% uses Rel to compare New with so far the best answer
update_bestof(Level,New,Old,Test):-
val(bestof,Level,Old),!,
Test,
bb_set(bestof,Level,New).
update_bestof(Level,New,_,_):-
bb_let(bestof,Level,New).
% ensure correct implementation of embedded calls to bestof/3
inc_level(Obj,X1):-val(Obj,Obj,X),!,X1 is X+1,bb_set(Obj,Obj,X1).
inc_level(Obj,1):-bb_def(Obj,Obj,1).
dec_level(Obj,X):-val(Obj,Obj,X),X>0,X1 is X-1,bb_set(Obj,Obj,X1).
Note that precomputation of Test in bestof/3 before calling the
workhorse bestof0/4 is much more efficient than using some form
of apply meta-predicate inside bestof0/4.
BLACKBOARD
BASED ABTRACT
DATA TYPES
We will describe some simple uses of the blackboard to implement efficiently some basic abstract data types.
They all use the saved/2 predicate instead of save_term/2.
Saved/2 does
basically the same work but also makes a call to the blackboard garbage
collector if necessary. The reader can find the code in the file lib.pl
of the BinProlog distribution.
push(Type,S,X):-
default_val(Type,S,Xs,[]),
saved([X|Xs],T),
set(Type,S,T).
pop(Type,S,X):-
default_val(Type,S,V,[]),
V=[X|Xs],
set(Type,S,Xs).
stack(Type,S,Xs):-val(Type,S,Xs).
default_val(X,Y,V,_):-val(X,Y,V),!.
default_val(X,Y,D,D):-def(X,Y,D).
vector_def(Name,Dim,Zero):- Max is Dim-1,
saved(Zero,BBVal),
for(I,0,Max), % generator for I from 0 to Max
let(Name,I,BBVal), % def/3 or set/3 if needed
fail.
vector_def(_,_,_).
vector_set(Name,I,Val):-saved(Val,BBVal),set(Name,I,BBVal).
vector_val(Name,I,Val):-val(Name,I,Val).
Building multi-dimensional arrays on these vectors is straightforward, by
defining an index-to-address translation function.
global_array_set(I,J,Val):-saved(Val,S),set(I,J,S).
BLACKBOARD
BASED PROBLEM
SOLVING
A COMPLETE
KNIGHT TOUR
% recommended use: ?-go(5).
go(N):-
time(_),
init(N,M), % prepare the chess board
knight(M,1,1),!, % finds the first complete tour
time(T),
write(time=T),nl,statistics,show(N). % shows the answer
% fills the blackboard with free logical variables
% representing empty cell on the chess board
init(N,_):-
for(I,1,N), % generates I from 1 to N nondeterministically
for(J,1,N), % for/3 is the same as range/3 in the Art of Prolog
bb_def(I,J,_NewVar), % puts a free slot in the hashing table
fail.
init(N,M):-
M is N*N. % returns the number of cells
% tries to make a complete knight tour
knight(0,_,_) :- !.
knight(K,A,B) :-
K1 is K-1,
val(A,B,K), % here we mark (A,B) as the K-th cell of the tour
move(Dx,Dy), % we try a next move nondeterministically
step(K1,A,B,Dx,Dy).
% makes a step and then tries more
step(K1,A,B,Dx,Dy):-
C is A + Dx,
D is B + Dy,
knight(K1,C,D).
% shows the final tour
show(N):-
for(I,1,N),
nl,
for(J,1,N),
val(I,J,V),
write(' '),X is 1-V // 10, tab(X),write(V),
fail.
show(_):-nl.
% possible moves of the knight
move( 2, 1). move( 2,-1). move(-2, 1). move(-2,-1).
move( 1, 2). move(-1, 2). move( 1,-2). move(-1,-2).
tak(X,Y,Z,A) :- X =< Y, !, Z = A.
tak(X,Y,Z,A) :-
X1 is X - 1,
Y1 is Y - 1,
Z1 is Z - 1,
ltak(X1,Y,Z,A1),
ltak(Y1,Z,X,A2),
ltak(Z1,X,Y,A3),
ltak(A1,A2,A3,A).
ltak(X,Y,Z,A):-
tak_encode(X,Y,XY),
tak_lemma(XY,Z,tak(X,Y,Z,A),A).
tak_encode(Y,Z,Key):-Key is Y<<16 \/ Z.
tak_decode(Key,Y,Z):-Y is Key>>16, Z is Key <<17>>17 .
%optimized lemma
A LINDA
STYLE INTERFACE
to the
BLACKBOARD
The following predicates show how to do some Linda-style operations on
top of the blackboard primitives.
% out/1, rd/1, in/1
out(Mes):-object(Obj),message(Id),out(Obj,Id,Mes).
rd(Mes):-object(Obj),message(Id),rd(Obj,Id,Mes).
in(Mes):- object(Obj),message(Id),in(Obj,Id,Mes).
% out/2, rd/2, in/2
out(Id,Mes):-object(Obj),out(Obj,Id,Mes).
rd(Id,Mes):-object(Obj),rd(Obj,Id,Mes).
in(Id,Mes):-object(Obj),in(Obj,Id,Mes).
% out/3, rd/3, in/3
out(Obj,Id,_):-val(Obj,Id,_),!,fail.
out(Obj,Id,Mes):-saved(Mes,Sent),let(Obj,Id,Sent).
rd(Obj,Id,Mes):-val(Obj,Id,Mes).
in(Obj,Id,Mes):-val(Obj,Id,Mes),rm(Obj,Id).
% eval/0, eval/1, eval/2
eval:-object(Obj),message(Id),eval(Obj,Id).
eval(Id):-object(O),eval(O,Id).
eval(Obj,Id):-val(Obj,Id,(Answer:-Goal)),Goal,!,
saved(Answer,NewAnswer),
set(Obj,Id,NewAnswer).
% tools
object(New):-var(New),!,val('$object','$object',New).
object(New):-atomic(New),let('$object','$object',New).
message(New):-var(New),!,object(O),val(O,'$message',New).
message(New):-atomic(New),object(O),let(O,'$message',New).
CONTINUATIONS
as FIRST
ORDER OBJECTS
CONTINUATIONS
MANIPULATION VS.
INTUITIONISTIC /LINAIRE
IMPLICATION
Using intuitionistic implication (in fact, it should be -:
but let's forget this for a moment) we can write in BinProlog:
insert(X, Xs, Ys) :-
paste(X) => ins(Xs, Ys).
ins(Ys, [X|Ys]) :- paste(X).
ins([Y|Ys], [Y|Zs]):- ins(Ys, Zs).
used to nondeterministically insert an element in a list,
the unit clause paste(X) is available only within the scope of the
derivation for ins. This gives:
?- insert(a,[1,2,3],X).
X=[a,1,2,3];
X=[1,a,2,3];
X=[1,2,a,3];
X=[1,2,3,a]
insert(X,Xs,Ys):-ins(Xs,Ys),paste(X).
ins(Ys,[X|Ys]) @@ paste(X).
ins([Y|Ys],[Y|Zs]):-ins(Ys,Zs).
Note that the element to be inserted is not passed to the recursive
clause of the predicate ins/2 (which becomes therefore simpler),
while the unit clause of the predicate ins/2 will communicate
directly with insert/3 which will directly `paste' the appropriate
argument in the continuation.
DIRECT BINAIRY
CLAUSE
PROGRAMMING
and
FULL-BLOWN
CONTINUATION Head ::- Body.
They give full power to the knowledgeable programmer on
the future of the computation. Note that such a facility is not
available in conventional WAM-based systems where continuations
are not first-order objects.
We can use them to write programs like:
member_cont(X,Cont)::-
strip_cont(Cont,X,NewCont,true(NewCont)).
member_cont(X,Cont)::-
strip_cont(Cont,_,NewCont,member_cont(X,NewCont)).
test(X):-member_cont(X),a,b,c.
A query like
?-test(X).
will return X=a; X=b; X=c; X=whatever follows from the calling point of test(X).
catch(Goal,Name,Cont)::-
lval(catch_throw,Name,Cont,call(Goal,Cont)).
throw(Name,_)::-
lval(catch_throw,Name,Cont,nonvar(Cont,Cont)).
where lval(K1,K2,Val) is a BinProlog
primitive which unifies Val with a backtrackable global logical
variable accessed by hashing on two (constant or variable)
keys K1,K2.
loop:-loop.
c(X):-b(X),loop.
b(hello):-throw(here).
b(bye).
go:-catch(c(X),here),write(X),nl.
Notice that due to its simple translation semantics this program
still has a first order reading and that BinProlog's lval/3
is not essential as it can be emulated by an extra argument passed
to all predicates.
BACKTRACKBLE
DESTRUCTIVE ASSIGNMENT
UPDATABLE LOGICAL
ARRAYS in
PROLOG:FIXING
the SEMANTICS
of
SETARG/3
Let us recall that setarg(I,Term,Value) installs
Value as the I-th argument of Term and takes care to
put back the old value found there on backtracking.
setarg(I,OldTerm,NewValue,NewTerm):-
functor(OldTerm,F,N),
functor(NewTerm,F,N),
arg(I,NewTerm,NewValue),
copy_args_except_I(N,I,OldTerm,NewTerm).
copy_args_except_I(0,_,_,_):-!.
copy_args_except_I(I,I,Old,New):-!,I1 is I-1,
copy_args_except_I(I1,I,Old,New).
copy_args_except_I(N,I,Old,New):-N1 is N-1,arg(N,Old,X),arg(N,New,X),
copy_args_except_I(N1,I,Old,New).
safe_arg(I,Term,Val):-arg(I,Term,'$box'(Val)).
safe_setarg(I,Term,Val):-setarg(I,Term,'$box'(Val)).
Using '$box'/1 in safe_arg/3 (safe_setarg/3)
ensures that cell I of the functor Term
will be indirectly accessed (updated) even if it contains a variable
which in a WAM would be created on place and therefore
it would be subject of unpredictable side-effects.
new_array(Size,Array):-
functor(Array,'$array',Size).
update_array(Array,I,Val):-
safe_setarg(I,Array,Val).
access_array(Array,I,Value):-
safe_arg(I,Array,Value).
We suggest to use this ADT in your program instead of basic setarg
when performance is not an absolute requirement.
% fold,foldl based on safe failure driven destructive change_arg
foldl(Closure,Null,List,Final):-fold(Closure,Null,X^member(X,List),Final).
fold(Closure,Null,I^Generator,Final):-
fold0(s(Null),I,Generator,Closure,Final).
fold0(Acc,I,Generator,Closure,_):-
term_append(Closure,args(SoFar,I,O),Selector),
Generator,
arg(1,Acc,SoFar),
Selector,
change_arg(1,Acc,O),
fail.
fold0(Acc,_,_,_,Final):-
arg(1,Acc,Final).
?- foldl(+,0,[1,2,3],R).
?- fold(*,1,X^member(X,[1,2,3]),R).
'INVISIBLE'
GRAMMARS
% tools
begin_dcg(Name,Xs):-lval(dcg,Name,Xs-Xs).
end_dcg(Name,Xs):-lval(dcg,Name,Xs-[]).
w(Word,Name):-
lval(dcg,Name,State),
State=_-[Word|Xs2],
setarg(2,State,Xs2).
begin_dcg(Xs):-begin_dcg(default,Xs).
end_dcg(Xs):-end_dcg(default,Xs).
w(Word):-w(Word,default).
% grammar
x:-ng,v.
ng:-a,n.
a:-w(the).
a:-w(a).
n:-w(cat).
n:-w(dog).
v:-w(walks).
v:-w(sleeps).
% test
go:-begin_dcg(Xs),x,end_dcg(Ys),write(Ys),nl,fail.
?- go.
[the,cat,walks]
[the,cat,sleeps]
[the,dog,walks]
[the,dog,sleeps]
[a,cat,walks]
[a,cat,sleeps]
[a,dog,walks]
[a,dog,sleeps]
dcg_connect/1 % works like 'C'/3 with 2 hidden arguments
dcg_def/1 % sets the first hidden DCG argument
dcg_val/1 % retrives the current state of the DCG stream
dcg_tell/1 % focus on a given DCG stream (from 0 to 255)
dcg_telling/1 % returns the number of the current DCGs stream
% INVISIBLE DCG connect operation: normally macro-expanded
'#'(Word):-dcg_connect(Word).
% example: ?-dcg_phrase(1,(#a,#b,#c),X).
dcg_phrase(DcgStream,Axiom,Phrase):-
dcg_telling(X),dcg_tell(DcgStream),
dcg_def(Phrase),
Axiom,
dcg_val([]),
dcg_tell(X).
% grammar
ax:-ng,v.
ng:-a,n.
a:-dcg_connect(the).
a:-dcg_connect(a).
n:-dcg_connect(cat).
n:-dcg_connect(dog).
v:-dcg_connect(walks).
v:-dcg_connect(sleeps).
go:-
st(H,T),
dcg_def(Xs),ax,dcg_val([]),
pp(Xs,H,T),nl,
fail.
st(H,T):-statistics(global_stack,[H,_]),statistics(trail,[T,_]).
pp(O,H0,T0):-
st(H1,T1),T is T1-T0,H is H1-H0,
write([heap=H,trail=T]),write('==>'),write(O),nl,fail.
pp(_,_,_).
it will print out something like:
[heap = 308,trail = 44]==>[the,cat,walks]
[heap = 308,trail = 32]==>[the,cat,sleeps]
[heap = 308,trail = 32]==>[the,dog,walks]
[heap = 308,trail = 20]==>[the,dog,sleeps]
[heap = 308,trail = 44]==>[a,cat,walks]
[heap = 308,trail = 32]==>[a,cat,sleeps]
[heap = 308,trail = 32]==>[a,dog,walks]
[heap = 308,trail = 20]==>[a,dog,sleeps]
BINPROLOG'S
C-INTERFACE
A DEMO
PROGRAM
Starting with version 3.39 the C-interface has been moved in
directory c_inter. The discussion which follows basically explains
the content of the files c.c c.h and c.pl in this directory.
CALLING C
from
BINPROLOG:
ADDING NEW
BUILTINS
New builtins are added in headers.pl and after a "make realclean; make"
a new version of BinProlog containing them is generated automatically.
Starting from headers.pl the system will first generate the files
defs.h, prof.h, builtins.pl and then recompile itself.
For this recompilation either a previous version of
BinProlog(7)or SICStus Prolog is used.
b0(+/3,arith(1),in_body). % arity=3+1 by binarization
Notice that arith(1) means that it is "like an arithmetic functions" which returns one
value, i.e this should be used as in
+(0,1,Result).
I would suggest to start with the simplest form of interaction:
a call of your own C-code from BinProlog. Try modifying in the file
c.c (provided with the C-sources of BinProlog,
in directory c_inter),
the function new_builtin() which looks as follows:
/*
New builtins can be called from Prolog as:
new_builtin(0,
CALLING
PROLOG from
C
Normally this is done after a first call from Prolog to C
(this gives a chance to the prolog system to get initialized).
The most flexible technique is based on mutiple engines. Take a look
at case 11 and case 12 in the previous section.
/* this can be used to call Prolog from C : see example if0 */
term bp_prolog_call(goal,regs,H,P,A,wam)
register term goal,regs,H,*A;
register instr P;
register stack wam;
{
PREP_CALL(goal);
return bp(regs,H,P,A,wam);
}
/* simple example of prolog call */
term if0(regs,H,P,A,wam)
register term regs,H,*A;
register instr P;
register stack wam;
{ term bp();
cell goal=regs[1];
/* in this example the input GOAL is in regs[1] */
/* of course you can also build it directly in C */
/* unless you want specific action on failure,
use BP_prolog_call(goal) here */
H=bp_prolog_call(goal,regs,H,P,A,wam);
if(H)
fprintf(stderr,"success: returning from New WAM\n");
else
fprintf(stderr,"fail: returning from New WAM\n");
/* do not forget this !!! */
return H; /* return NULL to signal failure */
}
EXAMPLE
PROGRAMS
allperms.pl: permutation benchmarks with findall
bestof.pl: an implementation of bestof/3
bfmeta.pl: breadth-first metainterpreter
bp.pl: float intensive neural net learning by back-propagation
cal.pl: calendar: computes the last 10000 fools-days
fcal.pl: calendar: with floats
chat.pl: CHAT parser
choice.pl: Choice-intensive ECRC benchmark
cnrev.pl: nrev with ^/2 as a constructor instead of ./2
cube.pl: E. Tick's benchmark program
fibo.pl: naive Fibonacci
ffibo.pl: naive Fibonacci with floats
hello.pl: example program to create stand-alone Unix application
knight.pl: knight tour to cover a chess-board (uses the bboard)
lknight.pl: knight tour to cover a chess-board (uses the lists)
ltak.pl: tak program with lemmas
lfibo.pl: fibo program with lemmas
lq8.pl : 8 queens using global logical variables
maplist.pl: fast findall based maplist predicate
nrev.pl: naive reverse
nrev30.pl: small nrev benchmark to reconsult for the meta-interpreter
or.pl: or-parallel simulator for binary programs (VT100)
other_bm*: benchmark suite to compare Sicstus, Quintus and BinProlog
puzzle.pl: king-prince-queen puzzle
q8.pl: fast N-queens
qrev.pl: quick nrev using builtin det_append/3
subset.pl: findall+subset
tetris.pl: tetris player (VT100)
RELATED WORK
REFERENCES
APPENDIX
!/0
(#)/1
(#)/3
* /3
(+)/2
(+)/3
(++)/3
(,)/2
(-)/2
(-)/3
(-:)/2
(->)/2
(.)/2
(.)/3
/ /3
// /3
(/\)/3
(:)/2
(:=:)/2
(;)/2
(<)/2
<< /3
(=)/2
(=..)/2
(=:=)/2
(=<)/2
(==)/2
(=>)/2
(=\=)/2
(>)/2
(>=)/2
>> /3
(@<)/2
(@=<)/2
(@>)/2
(@>=)/2
C/3
(\)/2
(\)/3
(\+)/1
(\/)/3
(\=)/2
(\==)/2
^ /2
abolish/2
abort/0
acos/2
add_cont/3
add_instr/4
add_true/2
all/3
all_but_at_least/4
append/3
appendN/2
append_conj/3
append_disj/3
apropos/1
apropos/2
arg/3
argn/3
arith_dif/2
arith_eq/2
asin/2
ask_engine/2
asm/0
asm/1
assert/1
asserta/1
assertz/1
assumed/1
assumei/1
assumel/1
atan/2
atom/1
atomic/1
bagof/3
bb/0
bb/1
bb_change_arg/3
bb_def/2
bb_def/3
bb_element/2
bb_get/4
bb_let/2
bb_let/3
bb_list/1
bb_list0/3
bb_op/3
bb_put/2
bb_reset/0
bb_rm/1
bb_rm/2
bb_set/2
bb_set/3
bb_val/2
bb_val/3
boot/0
bu0/3
bu1/1
bu_ctr/2
call/1
change_arg/3
char_in_cmd/2
clause/2
cmake/0
cmake/1
cmake/2
cnl/0
co/0
compare/3
compare0/3
compile/1
compound/1
consult/1
copy_term/2
copy_term/3
cos/2
create_engine/4
ctime/1
current_module/1
current_op/3
current_predicate/1
current_predicate/2
cwrite/1
dcg_connect/1
dcg_def/1
dcg_phrase/2
dcg_phrase/3
dcg_tell/1
dcg_telling/1
dcg_val/1
debug/1
def/3
destroy_engine/1
det_append/3
det_append0/3
dir/0
display/1
do_body/1
drop_at_least/2
(dynamic)/1
ed/0
edit/0
edit/2
emacs/0
erase/1
errmes/2
exists_file/1
exp/2
expand_term/2
expr/2
fail/0
false/0
fast_write/1
file_extension_list/1
file_library/2
file_search_path/1
find_at_most/4
find_file/2
find_while/4
findall/3
findall/4
findall_conj/3
findall_conj/4
findall_disj/3
findall_disj/4
findall_load_heap/1
findall_store_heap/1
findall_workhorse/5
float/1
float/2
float_fun/3
float_fun2/4
flush/0
for/3
free_variables/4
functor/3
gc_call/1
gc_read/1
gc_read_clause/1
gensym/2
get/1
get0/1
get_code/1
greater/2
greater_eq/2
ground/1
halt/0
halt/1
has_fuel/1
help/1
if/3
if0/3
include/1
init_gensym/1
input_float/4
instance/2
integer/1
integer/2
interactive/1
(is)/2
is_assumed/1
is_builtin/1
is_compiled/1
is_dynamic/1
is_module/1
is_prolog/1
is_public/1
iso_close_stream/2
iso_eof/1
iso_get_byte/2
iso_lseek/4
iso_open_stream/3
iso_peek_byte/2
iso_put_byte/2
iso_read_term/3
iso_write_term/3
ith_clause/4
kcmake/0
keysort/2
kmake/0
length/2
less/2
less_eq/2
let/3
lift_heap/2
list2term/2
list_asm/3
listing/0
listing/1
listing/2
log/2
log/3
ls/0
lval/3
lwrite/1
main/1
make/0
make/1
make/2
make/4
make/5
make0/4
make_appl/1
make_cmd/2
make_executable_unix_appl/3
make_file_name/4
member/2
member_i/4
meta_interpreter/1
metacall/1
mod/3
(module)/1
(module)/2
module_call/2
module_name/3
module_predicate/3
modules/1
my_edit/1
name/2
new_builtin/3
nl/0
nonvar/1
(nospy)/1
(not)/1
notepad/0
nth_answer/2
nth_member/3
number/1
numbervars/3
older_file/2
op/3
op0/3
or/2
otherwise/0
patch_it/4
phrase/2
phrase/3
pico/0
portable_display/1
portray/1
portray_clause/1
pow/3
pp_clause/1
pp_term/1
predicate_property/2
print/1
profile/0
(public)/1
put/1
put_code/1
quiet/1
quietmes/1
random/1
read/1
read_clause/1
read_term/2
read_tokens/2
read_with_singletons/3
reconsult/1
repeat/0
restart/0
retract/1
retractall/1
rm/2
saved/2
see/1
see_at/1
see_tell/2
see_tell_at/2
seeing/1
seeing_at/1
seeing_telling/2
seeing_telling_at/2
seen/0
seen_told/1
self_info/1
set/3
set_c_threshold/1
set_c_trace/1
setarg/3
setof/3
setref/2
shell/1
show_code0/2
sin/2
skip_until/2
skip_when/2
sort/2
(spy)/1
spy_goal/1
spying/1
sqrt/2
sread/2
load_engine/3
stat0/3
stat_dict/2
statistics/0
statistics/2
string_op/3
strip_cont/3
strip_cont0/2
swrite/2
symcat/3
system/1
tab/1
take_at_most/2
tan/2
tell/1
tell_at/1
telling/1
telling_at/1
term2list/4
term_append/3
term_chars/2
termcat/3
textedit/0
told/0
top_read_term/2
toplevel/0
tr_body/2
trace/1
true/0
ttyin/1
ttynl/0
ttyout/1
ttyprin/1
ttyprint/1
ttyput/1
ttywrite/1
ttywriteln/1
unix/1
unix_access/2
unix_argc/1
unix_argv/2
unix_cd/1
unix_getenv/2
unix_kill/2
user_error/2
val/3
var/1
vars_of/2
vi/0
warn_singletons/3
while/2
write/1
write_float/1
writeq/1
:-op(1000,xfy,',').
:-op(1100,xfy,(';')).
:-op(1200,xfx,('-->')).
:-op(1200,xfx,(':-')).
:-op(1200,fx,(':-')).
:-op(700,xfx,'is').
:-op(700,xfx,'=').
:-op(500,yfx,'-').
:-op(500,fx,'-').
:-op(500,yfx,'+').
:-op(500,fx,'+').
:-op(400,yfx,'/').
:-op(400,yfx,'*').
:-op(650,xfy,'.').
:-op(700,xfx,'>=').
:-op(700,xfx,'>').
:-op(700,xfx,'=<').
:-op(700,xfx,(<)).
:-op(700,xfx,(=\=)).
:-op(700,xfx,(=:=)).
:-op(300,fy,(~)).
:-op(200,xfy,(^)).
:-op(300,xfx,(mod)).
:-op(400,yfx,(>>)).
:-op(400,yfx,(<<)).
:-op(400,yfx,(//)).
:-op(500,yfx,(#)).
:-op(500,fx,(#)).
:-op(500,yfx,(\/)).
:-op(500,yfx,(/\)).
:-op(700,xfx,(@>=)).
:-op(700,xfx,(@=<)).
:-op(700,xfx,(@>)).
:-op(700,xfx,(@<)).
:-op(700,xfx,(\==)).
:-op(700,xfx,(==)).
:-op(700,xfx,(=..)).
:-op(700,xfx,(\=)).
:-op(900,fy,(not)).
:-op(900,fy,(\+)).
:-op(900,fx,(spy)).
:-op(900,fx,(nospy)).
:-op(1050,xfy,(->)).
:-op(1050,xfx,(@@)).
:-op(1150,fx,(dynamic)).
:-op(1150,fx,(public)).
:-op(1150,fx,(module)).
:-op(1200,xfx,(::-)).
:-op(900,yfx,(:)).
:-op(600,xfx,(:=:)).
:-op(950,xfy,(-:)).
:-op(950,xfy,(=>)).
:-op(600,xfx,(<=)).
:-op(700,xfx,(=:)).
:-op(700,xfx,(:=)).
(1)
BinProlog Copyright (C) Paul Tarau 1992-94 All rights reserved
(2)
Take care if you use your own binary clauses to keep always
the continuation as a last argument of the last embedded continuation `predicate'.
Look at the asm/0 tracer how BinProlog itself does this.
(3)
Is/2 now accepts execution of any predicate of arity n+1 as a function
of arity n.
(4)
This is usually better to be left to a system person who also can ensure that users
inherit the BP_PATH environment variable. An individual user
can also put something like setenv BP_PATH /usr/local/BinProlog
his or her .cshrc or .login file, to access the shared
BinProlog programs.
(5)
We suggest avoiding these operations because they are not garbage collection-safe in
BinProlog 4.00.
(6)
A further ambiguity in some implementations of setarg/3 comes from the fact
that it is not clear if the location itself or its dereferenced contents should be mutated
(7)
Use "make remake" if BinProlog is the only Prolog around.