I would like to build my own coroutines in Prolog.
I'd like to add some extra functionalities.
Writing a vanilla interpreter for coroutines should be on the teaching list of every Prolog course. It is quite simple, here you see the normal vanilla interpreter, simplified:
% solve(+Term)
solve(true).
solve((A,B)) :- solve(A), solve(B).
solve(H) :- clause(H, B), solve(B).
Now for corouting, in the sense of suspending goals via freeze/2, just add an additional input output parameter pair with the delayed goals, for a specification of select/3 see (*):
% solve(+Term, +List, -List)
solve(G, L, R) :- select(freeze(V, F), L, H),
nonvar(V), !,
solve((F,G), H, R).
solve(freeze(V, G), L, [freeze(V,G)|L]) :- var(V), !.
solve(freeze(_, G), L, R) :- solve(G, L, R).
solve(true, L, L).
solve((A,B), L, R) :- solve(A, L, H), solve(B, H, R).
solve(H, L, R) :- clause(H, B), solve(B, L, R).
You can use the above vanilla interpreter to study different wake-up strategies. I am not sure whether it captures existing Prolog systems. But you could run examples such as:
?- freeze(X, member(X, [the(1), the(2)])), X = the(Y).
successfully, by posing the following question:
?- solve((freeze(X, member(X, [the(1), the(2)])), X = the(Y), true), [], L).
The ", true" is needed to check a last time for woken up goals. If L is returned empty then all frozen goals where woken up. Otherwise there are some pending frozen goals. Which is sometimes called floundering.
The above prototype also leads to a natural implementation of coroutines via thin attributes, the undo/1 and some little support of the interpreter by a goal injection queue. I will post about this soon somewhere else.
Bye
(*)
https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue
One possible solution would be to use the term-expansion mechanism provided by some Prolog systems and Logtalk to rewrite calls to e.g. the freeze/2 predicate to do the extra steps you want. One must be careful, however, to not expand a call to a predicate into another goal that calls the same predicate as goal-expansion is recursively applied until a fixed-point is reached. The Logtalk implementation of the term-expansion mechanism makes it easy to avoid this trap (with the additional advantage of portability as you can use Logtalk with most Prolog systems) by using a compiler bypass control construct, {}/1. A silly example would be:
:- object(my_expansions,
implements(expanding)).
goal_expansion(
freeze(Var,Goal),
( write('If you instantiate me, I will run away!\n'),
{freeze(Var,Goal)}, % goal will not be further expanded
write('Bye!\n')
)
).
:- end_object.
This object can then be used as an hook object for the compilation of source files containing calls to freeze/2 that you want to expand. Something like (assuming that the object above is saved in a file with the name my_expansions.lgt and that the source file that you want to expand is named source.lgt):
?- logtalk_load(my_expansions), logtalk_load(source, [hook(my_expansions)]).
For full details see the Logtalk documentation and examples.
There might be a clean way that I'm not aware of doing the same using the a Prolog system own term-expansion mechanism implementation. Anyone?
Related
In my program, I use a simple recursive clause/2 to track my proving.
prove(X):-
clause(X, B),
B == true.
prove((X1, X2)):-
prove(X1),
prove(X2).
prove(X):-
X \= true,
X \= (_, _),
clause(X, B),
write(X), write(' <= '), write(B), nl,
prove(B).
What's more, I'm trying to use tabling to solve the exchangeability/transitivity problems. (I'm talking about f(a)=b <=> f(b)=a, f(a)=b, f(b)=c => f(a)=c, etc).
I know using transistive closure or a decorated predicate can help to solve this. However, I'm working on a lot of rules. If I do the same stuff for each group of rules, it would be a huge workload for me, and indeed, contributes to a lot of bugs right now.
However, firstly clause is tracing back to start_tabling.
[1] ?- prove(segment_equal(c,d,a,b)).
segment_equal(c,d,a,b) <= start_tabling(user:segment_equal(c,d,a,b),segment_equal tabled(c,d,a,b))
Secondly, it will raise Errors.
ERROR: No permission to access private_procedure `'$tbl_variant_table'/3'
So I'm wondering if I can use tabling to save my codes and track the procedure at the same time? Or any other better practice?
PS: If background provides any help, I'm working on a geometry automated theorem prover, as we known, those basic rules appear in many theorems, like segment equal, angle equal, etc.
A possible solution is to use the predicate_property/2 predicate to exclude which predicates that should not be traced. For example, the following meta-interpreter will only trace calls to dynamic predicates:
prove(X) :-
clause(X, B),
B == true.
prove((X1, X2)) :-
prove(X1),
prove(X2).
prove(X) :-
clause(X, B),
( predicate_property(B, dynamic) ->
write(X), write(' <= '), write(B), nl
; true
),
prove(B).
Depending on the used Prolog system, there may be other reflection predicates that would allow you to filter what should be traced. E.g. only predicates defined in whitelisted files.
Firstly, I'm not a prolog programmer. I'm doing an assignment for school where we build a small expert system. I've chosen to use prolog for the assignment and I'm following this book: http://www.amzi.com/distribution/files/xsip_book.pdf. Specifically I'm looking at chapter 2.
There is a procedure in this book that I would like to use called "ask." It can be found on page 14. Ask, uses another procedure called "known" to remember the answers to questions. I have basically copied this procedure for use in my code, but I am getting an existence error related to the "known" procedure. Not being a prolog programmer, I don't know how to debug it. Hopefully someone can help.
Here is a sample run of my code:
| ?- species(X).
uncaught exception: error(existence_error(procedure,known/3),ask/2)
Here is the code:
species(limba) :- %There are a bunch of these
distribution(west_africa),
color(tan_with_black_streaks),
figure(plain),
janka_hardness(670),
workability(easy).
distribution(X) :- ask(distribution, X).
color(X) :- ask(color, X).
figure(X) :- ask(figure, X).
janka_hardness(X) :- ask(janka_hardness, X).
workability(X) :- ask(workability, X).
ask(A, V) :-
known(yes, A, V),
!.
ask(A, V) :-
known(_, A, V),
!,
fail.
ask(A, V) :-
write(A:V),
write('? : '),
read(Y),
asserta(known(Y, A, V)),
Y == yes.
This program expects known/3 to be dynamic (and the book you pointed to says this). That is, it's created and managed at run time. The Prolog documentation tells you how to declare a dynamic predicate. You might want to read through it. – lurker
Why this program answers False in SWI-PROLOG?
sor(x, y):- sorted(y), perm(x, y).
sorted([]).
sorted([x, []]).
sorted([x, y, z]):- mi(x, y), sorted([y, z]).
perm([], []).
perm([x,y],[u,v]):- delete(u,[x,u],z), perm(z,v).
delete(x,[x,y],y].
delete(x, [y, z], [y, w]):- delete(x,z,w).
mi(0, x).
mi(s(x), s(y)):- mi(x, y).
for the query ?-
sor([s(s(s(s(s(0))))), s(s(s(s(s(s(0)))))), s(s(s(0))), s(s(0)), []], y).
This is an adaptation to SWIProlog of an inefficient sorting-program used as example in the book Foundations of Logic Programming, by Loyd (you can find the original SLOWSORT program example in this pdf, on page 9)
SWI Prolog is a standard Prolog, isn't it?
Edit
Now I have tried to correct the program (looking a little to the lists syntax in Prolog)
sor(X, Y):- perm(X, Y), sorted(Y).
sorted([]).
sorted([X|[]]).
sorted([X|[Y|Z]]):- mi(X, Y), sorted([Y|Z]).
perm([], []).
perm([X|Y],[U|V]):- delete(U,[X|Y],Z), perm(Z, V).
delete(X,[X|Y],Y).
delete(X, [Y|Z], [Y|W]):- delete(X, Z, W).
mi(0, X).
mi(s(X), s(Y)):- mi(X, Y).
and changing the query in
sor([s(s(s(s(s(0)))))|[ s(s(s(s(s(s(0))))))|[s(s(s(0)))|[ s(s(0))|[]]]]], Y).
Well, Prolog now gives success, but it gives this substitution
Y = [s(s(0)), s(s(s(0))), s(s(s(s(s(0))))), s(s(s(s(s(s(...))))))]
and I don't understand the meaning of (...): Why not (0)?
Edit2
I notice that after giving the command swipl -s slowsort.pl I obtain this error message
Warning: /home/navigazione/Scrivania/slowsort.pl:3:
Singleton variables: [X]
Warning: /home/navigazione/Scrivania/slowsort.pl:9:
Singleton variables: [X]
It seems to refer to 3th and 9th rows of the program, but I don't understand what it means.
Great, you managed to translate it to correct Prolog :)
What you see is the top level trying to make things readable by omitting stuff (the ... means there is stuff there that is not shown). See this question and answers for different ways you can tell the top level to show the complete term instead of hiding parts of it.
As for the singleton variable warnings, it just tells you that you have logical variables (on lines 3 and 9) that you have only mentioned once in their syntactical scope. You can write _X instead of X to make it explicit that you are not using the value of the variable in that scope.
ISO-Prolog (ISO/IEC 13211-1:1995 including Cor.1:2007, Cor.2:2012) offers the following built-in predicates for testing the type of a term:
8.3 Type testing
1 var/1. 2 atom/1. 3 integer/1. 4 float/1. 5 atomic/1. 6 compound/1. 7 nonvar/1. 8 number/1. 9 callable/1. 10 ground/1. 11 acyclic_term/1.
Within this group there are those whose purpose is solely to test for a certain instantiation, that is 8.3.1 var/1, 8.3.7 nonvar/1, 8.3.10 ground/1, and those that assume that a term is sufficiently instantiated such that the type test is safe. Unfortunately, they are combined with testing for a concrete instantiation.
Consider the goal integer(X) which fails if X is a nonvar term that is not an integer and when X is a variable. This destroys many desirable declarative properties:
?- X = 1, integer(X).
true.
?- integer(X), X = 1.
false.
Ideally the second query would either succeed using some form of coroutining ; or it would issue an instantiation error1 according to the error classification. After all:
7.12.2 Error classification
Errors are classified according to the form of Error_term:
a) There shall be an Instantiation Error when an
argument or one of its components is a variable, and an
instantiated argument or component is required. It has
the form instantiation_error.
...
Note that this implicit combination of instantiation testing and type testing leads to many errors in Prolog programs and also here on SO.
A quick fix to this situation would be to add an explicit test in front of every test built-in, either verbosely as
( nonvar(T) -> true ; throw(error(instantiation_error,_)) ),
integer(T), ....
or more compactly as
functor(T, _,_),
integer(T), ....
it could be even
T =.. _,
integer(T), ...
My question is twofold:
How to provide this functionality on the user level?
and, to make this also a bit challenging:
What is the most compact implementation of a safer atomic/1 written in ISO-Prolog?
1 Other less desirable options would be to loop or to produce a resource error. Still preferable to an incorrect result.
The testing for types needs to distinguish itself from the traditional "type testing" built-ins that implicitly also test for a sufficient instantiation. So we effectively test only for sufficiently instantiated terms (si). And if they are not sufficiently instantiated, an appropriate error is issued.
For a type nn, there is thus a type testing predicate nn_si/1 with the only error condition
a) If there is a θ and σ such that nn_si(Xθ) is
true and nn_si(Xσ) is false —
instantiation_error.
atom_si(A) :-
functor(A, _, 0), % for the instantiation error
atom(A).
integer_si(I) :-
functor(I, _, 0),
integer(I).
atomic_si(AC) :-
functor(AC,_,0).
list_si(L) :-
\+ \+ length(L, _), % for silent failure
sort(L, _). % for the instantiation error
This is available as library(si) in Scryer.
In SWI, due to its differing behavior in length/2, use rather:
list_si(L) :-
'$skip_list'(_, L, T),
functor(T,_,_),
T == [].
This is a very naive attempt at implementing both your suggested solutions.
First, has_type(Type, Var) that succeeds, or fails with an instantiation error:
has_type(Type, X) :-
var(X), !,
throw(error(instantiation_error, _)).
has_type(Type, X) :-
nonvar_has_type(Type, X).
nonvar_has_type(atom, X) :- atom(X).
nonvar_has_type(integer, X) :- integer(X).
nonvar_has_type(compound, X) :- compound(X).
% etc
Second, a could_be(Type, Var) (analogy to must_be/2) that uses coroutining to allow the query to succeed at some point in the future:
could_be(Type, X) :-
var(X), !,
freeze_type(Type, X).
could_be(Type, X) :-
nonvar_has_type(Type, X).
freeze_type(integer, X) :- freeze(X, integer(X)).
freeze_type(atom, X) :- freeze(X, atom(X)).
freeze_type(compound, X) :- freeze(X, compound(X)).
% etc
There are several weak points to this approach but your comments might help me understand the use cases better.
EDIT: On "types" in Prolog
Types in Prolog, as I understand them, are not "types": they are just information that can be queried at run time, and which exists because it is a useful leaky abstraction of the underlying implementation.
The only way I have been able to make practical use of a "type" has been to "tag" my variables, as in the compound terms number(1), number(pi), operator(+), date(2015, 1, 8), and so on. I can then put variables in there, write deterministic or semi-deterministic predicates, understand what my code means when I see it a week later....
So a free variable and an integer are just terms; mostly because, as your question very smartly points out, a free variable can become an integer, or an atom, or a compound term. You could use coroutining to make sure that a free variable can only become a certain "type" of term later, but this is still inferior to using compound terms, from a practical point of view.
It highly likely that I am confounding very different issues here; and to be honest, my experience with Prolog is limited at best. I just read the documentation of the implementation I am using, and try to find out the best way to use it to my advantage.
I have a database of facts like this:
li(a,2).
li(b,3).
li(b,1).
li(c,2).
li(d,1).
li(d,1).
I need to write a predicate more(+Let) that succeeds if it exists more than one fact li(Let,_).
For example the queries more(b) and more(d) will succeed, but more(a) and more(c) will not.
My idea was to check if li(Let,_) succeeds more than once, but I do not know how to do it.
Try findall/3:
findall(X, li(d,X), L), length(L,N), N>1.
Abstracting the d out and making a predicate is trivial. Right? :)
If you don't want to use any of the predicates like findall, you can change the representation of your knowledge - bring it down one level, so to speak:
my_knowledge(li, [a-2,b-3,b-1,c-2,d-1,d-1]).
and then you can use SWI Prolog's predicate select/3 to handle it:
select_knowledge(kn, key, R):-
my_knowledge(kn,L),
select_key(L,key,R).
select_key(L,K,R):-
select(K-X,L,L1) -> R=[X|R1], select_key(L1,K,R1)
; R = [].
You can rewrite the last predicate as basic recursion over lists, and then tweak it to stop after getting first N results.
more_than_once(Goal) :-
\+ \+ call_nth(Goal,2).
With call_nth/2 as defined in this answer.
The big advantage of this solution compared to the other solutions proposed is that it will succeed rapidly even if there is a very large sequence of answers. In fact, it will even succeed for an infinite sequence of answers:
?- more_than_once(repeat).
true.
?- more_than_once(between(1,100000,_)).
true.
(The implementation of call_nth/2 uses some non-standard, low-level built-ins of SWI. It is possible to avoid that, but with even more headache.)
SWI-Prolog has library(aggregate).
:- [library(aggregate)].
more(Key) :- aggregate_all(count, li(Key, _), C), C > 1.
test:
?- more(b).
true.
?- more(a).
false.
It's not very easy to learn, but useful to handle such common tasks. If you have a very large code base, then findall (and aggregate as well, that uses findall inside) could be inefficient, building a list only to count its elements.
Then you could use a side effect based predicate: in this related answer you'll find such utility. For max efficiency, see the comments, where is explained how to use nb_setval/nb_getval...