Prolog: "Vanilla" metainterpreter with builtins - prolog

This answer by Jan Burse shows one of the simplest implementations of a metainterpreter in Prolog:
solve(true) :- !.
solve((A,B)) :- !, solve(A), solve(B).
solve(H) :- clause(H,B), solve(B).
I would like to extend this interpreter so that it can call builtins. The vanilla one isn't able to handle calls such as solve(member(X, [1,2,3,4])). Is this possible using ISO predicates? If not, is it possible using SWI-Prolog predicates?

I think predicate_property/2 may be useful for your task.
As the name already implies, this predicate relates a predicate (head) to one ore more properties.
For example:
?- predicate_property((A,B), P).
P = interpreted ;
P = visible ;
P = built_in ;
P = static ;
P = imported_from(system) ;
etc.
From such properties, you can deduce whether a predicate is built-in, and the call it directly.
It is also available in SICStus.
Beware though: Not all built-in predicates retain their semantics when called directly. I think discussing what they are and how to interpret them would be well worth its own question.

Stackoverflow is refusing to accept my answer :) that was
Just call/1 them
Edit
For instance
?- [user].
solve(true) :- !.
|: solve((A,B)) :- !, solve(A), solve(B).
|: solve(H) :- clause(H,B), solve(B).
|: solve(T) :- call(T).
|: ^Dtrue.
?- solve(member(X, [1,2,3,4])).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4.
The only addition: solve(T) :- call(T).

Related

negation \+ and vanilla meta-interpreter

The following is the classic "textbook" vanilla meta-interpreter for prolog.
% simplest meta-interpreter
solve(true) :- !.
solve((A,B)):- !, solve(A), solve(B).
solve(A) :- clause(A,B), solve(B).
The following is simple program which establishes facts two relations which are "positive" and one relation which makes use of negation by failure \+.
% fruit
fruit(apple).
fruit(orange).
fruit(banana).
% colour
yellow(banana).
% Mary likes all fruit
likes(mary, X) :- fruit(X).
% James likes all fruit, as long as it is yellow
likes(james, X) :- fruit(X), yellow(X).
% Sally likes all fruit, except yellow fruit
likes(sally, X) :- fruit(X), \+ (yellow(X)).
The meta-interpeter can handle goals related to the first two relations ?-solve(likes(mary,X)) and ?- solve(likes(james,X)_.
However it fails with a goal related to the third relation ?- solve(likes(sally,X). The swi-prolog reports a stack limit being reached before the program crashes.
Question 1: What is causing the meta-interpreter to fail? Can it be easily adjusted to cope with the \+ negation? Is this related to the sometimes discussed issue of built-ins not being executed by the vanilla meta-interpreter?
Question 2: Where can I read about the need for those cuts in the vanilla meta-interpreter?
Tracing suggests the goal is being grown endlessly:
clause(\+call(call(call(call(yellow(apple))))),_5488)
Exit:clause(\+call(call(call(call(yellow(apple))))),\+call(call(call(call(call(yellow(apple)))))))
Call:solve(\+call(call(call(call(call(yellow(apple)))))))
Call:clause(\+call(call(call(call(call(yellow(apple)))))),_5508)
Exit:clause(\+call(call(call(call(call(yellow(apple)))))),\+call(call(call(call(call(call(yellow(apple))))))))
Call:solve(\+call(call(call(call(call(call(yellow(apple))))))))
Change solve(A) into:
solve(Goal) :-
writeln(Goal),
sleep(1),
clause(Goal, Body),
solve(Body).
... and we see:
?- solve_mi(likes(sally,X)).
likes(sally,_8636)
fruit(_8636)
\+yellow(apple)
\+call(yellow(apple))
\+call(call(yellow(apple)))
\+call(call(call(yellow(apple))))
...
clause/2 determines the body of \+yellow(apple) to be \+call(yellow(apple)), which is not a simplification.
Can use instead:
solve_mi(true) :-
!.
solve_mi((Goal1, Goal2)):-
!,
solve_mi(Goal1),
solve_mi(Goal2).
solve_mi(\+ Goal) :-
!,
\+ solve_mi(Goal).
solve_mi(Goal) :-
clause(Goal, Body),
solve_mi(Body).
Result in swi-prolog:
?- solve_mi(likes(sally,X)).
X = apple ;
X = orange ;
false.
I'm using solve_mi because solve conflicts with e.g. clpBNR, and I'm not using variable names A and B because they convey no meaning.
For understanding the cuts, I'd recommend gtrace, to see the unwanted unification with other goals that would otherwise take place.

Prolog meta-interpreters and single sided unification

I tried this vanilla interpreter:
solve(true) :- !, true.
solve(X is E) :- !, X is E.
solve((A,B)) :- !, solve(A), solve(B).
solve(H) :- clause(H,B), solve(B).
Can we use it to meta-interpret some code? I tried this code,
requires SWI-Prolog 8.3.19, which runs fine normally:
sumlist([X|Y], R) => sumlist(Y, H), R is X+H.
sumlist([], R) => R is 0.
?- sumlist([1,2,3],X).
X = 6.
?- sumlist(X,Y).
ERROR: No rule matches sumlist(_21604,_21606)
But meta-interpretation goes wrong. The reason is that clause/2
doesn’t know about rules that use single sided unification:
?- clause(sumlist(A,B),C).
A = [_22728|_22730],
C = (sumlist(_22730, _22736), B is _22728+_22736) ;
A = [],
C = (B is 0).
?- solve(sumlist([1,2,3],X)).
X = 6.
?- solve(sumlist(X,Y)).
SWI-Prolog wurde unerwartet beendet.
Is there a solution for meta-interpreters and single sided unification?
One way out of the dilemma and stay inside the ISO core standard, is to translate single sided unfication to a combination of nonvar/1, (=)/2 and (==)/2, like here:
?- clause(sumlist(X,Y),Z), write((sumlist(X,Y):-Z)), nl, fail; true.
sumlist(_A, _B) :- nonvar(_A), _A = [_C|_D], sumlist(_D, _E), _B is _C+_E
sumlist(_A, _B) :- nonvar(_A), _A = [], _B is 0
Of course we need to add the built-ins nonvar/1, (=)/2 and (==)/2 as well to the meta interpreter:
solve(true) :- !.
solve(X is E) :- !, X is E.
solve(nonvar(X)) :- !, nonvar(X).
solve(X == Y) :- !, X == Y.
solve(X = Y) :- !, X = Y.
solve((A, B)) :- !, solve(A), solve(B).
solve(H) :- clause(H, B), solve(B).
Meta-interpreting sumlist/2 now works fine:
?- solve(sumlist([1,2,3],X)).
X = 6
?- solve(sumlist(X,Y)).
No
But the translator might challenge a Prolog system concering clause indexing. It moves away the functors from the head into the body. So the Prolog system would need some body front indexing as pioneered by YAP and found in Jekejeke Prolog.
Open Source:
Yet Another Pattern Matcher
https://gist.github.com/jburse/a3517410a28b759ef44f72584f89aaf8#file-picat3-pl
Vanilla Interpreter, Expansion Solution
https://gist.github.com/jburse/a3517410a28b759ef44f72584f89aaf8#file-vanilla4-pl

Expanding Prolog clauses without parameters

I am writing a program that transforms other programs by expanding predicates. I usually do this using clause/2, but it doesn't always expand a predicate if it has no parameters:
:- set_prolog_flag('double_quotes','chars').
:- initialization(main).
main :- clause(thing,C),writeln(C).
% this prints "true" instead of "A = 1"
thing :- A = 1.
Is it possible to expand predicates that have no parameters?
Some general remark: Note that this code is highly specific to SWI. In other systems which are ISO conforming you can only access definitions via clause/2, if that predicate happens to be dynamic.
For SWI, say listing. to see what is happening.
?- assert(( thing :- A = 1 )).
true.
?- listing(thing).
:- dynamic thing/0.
thing.
true.
?- assert(( thing :- p(A) = p(1) )).
true.
?- assert(( thing(X) :- Y = 2 )).
true.
?- listing(thing).
:- dynamic thing/0.
thing.
thing :-
p(_)=p(1).
:- dynamic thing/1.
thing(_).
true.
It all looks like some tiny source level optimization.

Depending on faliure to prove in prolog

Consider this code
:- use_module(library(clpfd)).
p(1).
p(3).
p(5).
p(7).
predecessor(A, B) :- A #= B - 1. % is true for pairs
q(X) :- predecessor(P, X), \+ p(P).
If I query ?- p(X) I correctly get the results
?- p(X).
X = 1 ;
X = 3 ;
X = 5 ;
X = 7.
But if I query ?- q(X) then I get false.
I realize that \+ is really not negation but faliure to prove, but what if not being able to prove something is sufficient for another predicate being true?
I wanted to give a reasonable use case / example which is why I resorted to using clpfd. Even without using it, I have another example which I can present:
likes(betty, butter).
likes(betty, jam) :- fail.
dislikes(betty, Item) :- \+ likes(betty, Item).
This example too, has a shortcoming that likes(betty, jam) :- fail. isn't really doing anything. But I hope I'm able to get my point across.
Is there a way in prolog to define this dependence?
You have to specifically define the "negative universe" of possibilities if you want Prolog to provide solutions in that space.
For instance, \+ p(X) cannot tell you specific values of X because the possible X that meet this criteria have not been defined. You're asking Prolog to invent what X might possibly be, which it cannot do.
You could define the universe of all possible values, then you can define what \+ p(X) means:
:- use_module(library(clpfd)).
p(1).
p(3).
p(5).
p(7).
predecessor(A, B) :- A #= B - 1. % is true for pairs
q(X) :- predecessor(P, X), P in 0..9, label([P]), \+ p(P).
Then you get:
2 ?- q(X).
X = 1 ;
X = 3 ;
X = 5 ;
X = 7 ;
X = 9 ;
X = 10.
3 ?-
Here we've told Prolog that the possible universe of P to choose from is defined by P in 0..9. Then the call \+ p(P) can yield specific results. Unfortunately, using \+, you still have to apply label([P]) before testing \+ p(P), but you get the idea.
In your other example of likes, it's the same issue. You defined:
likes(betty, butter).
likes(betty, jam) :- fail.
As you indicated, you wouldn't normally include likes(betty, jam) :- fail. since failure would already occur due to lack of a successful fact or predicate. But your inclusion is really an initial attempt to define the universe of possible choices. Without that definition, Prolog cannot "invent" what to pick from to test for a dislike. So a more complete solution would be:
person(jim).
person(sally).
person(betty).
person(joe).
food(jam).
food(butter).
food(eggs).
food(bread).
likes(betty, butter).
Then you can write:
dislikes(Person, Food) :-
person(Person),
food(Food),
\+ likes(Person, Food).

Return vowels from word

I've got this work to do and my teacher gave me a prolog file with the following facts:
vowel(a).
vowel(e).
vowel(i).
vowel(o).
vowel(u).
consonant(b).
consonant(c).
consonant(d).
consonant(f).
consonant(g).
consonant(h).
consonant(j).
consonant(k).
consonant(l).
consonant(m).
consonant(n).
consonant(p).
consonant(q).
consonant(r).
consonant(s).
consonant(t).
consonant(v).
consonant(w).
consonant(x).
consonant(y).
consonant(z).
And I need to create a rule that is able to return the vowels. How can I do that?
The output would be something like this:
blafoo([s,a,r,a], X).
X = [a].
I can't use any prolog predicate.
If you are mentioning both vowel/1 and consonant/1, you might be expected to write a pure, monotonic version. After all, why do you mention consonant/1?
word_vowels([], []).
word_vowels([C|Xs], Vs) :-
consonant(C),
word_vowels(Xs, Vs).
word_vowels([V|Xs], [V|Vs]) :-
vowel(V),
word_vowels(Xs, Vs).
?- word_vowels([a,m,a,z,o,n],Vs).
Vs = [a,a,o]
; false.
Alternatively using tfilter/3:
vowel_truth(C,true) :-
vowel(C).
vowel_truth(V,false) :-
consonant(V).
?- tfilter(vowel_truth,[a,m,a,z,o,n],Vs).
Vs = [a,a,o]
; false.
setof/3 could be a good choice here:
?- setof(X,(member(X,[a,m,a,z,o,n]),vowel(X)),L).
L = [a, o].
If your Prolog implements it, I would go with standard ISO sub_atom/5
?- W = amazon, sub_atom(W, _,1,_, C).
W = amazon,
C = a ;
W = amazon,
C = m ;
...
then
blafoo(Word, Wovel) :- sub_atom(Word, _,1,_, Wovel), vowel(Wovel).
edit after comment
Prolog doesn't 'returns' things, but you could always use a more appropriate naming and implementation for the relation, for instance like
word_vowels(Word, Wovels) :-
findall(Wovel, (sub_atom(Word, _,1,_, Wovel), vowel(Wovel)), Wovels).

Resources