Prolog meta-interpreters and single sided unification - prolog

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

Related

How to link constant with variable by assert?

I want to add in the DB a constant and a linked variable:
?- assertz(my(x, A))
So that in the future I can define A and get the only one entry. Sth like that:
?- assertz(my(x, A)), ..., A = 2.
?- my(A, B).
A = x,
B = 2.
Can this be done?
As I noted in the comments your idea of a link like a pointer is not the way to approach solving your problem.
A common solution is to walk the tree and construct a new tree as you walk the tree by replacing the leaf of the tree with a new leaf that contains the value from the input tree along with the associated value, what you are thinking should be linked.
Since you are somewhat new to Prolog I will do this with two examples. The first will just walk a tree and only return true when successfully walked. It can be used to understand how to walk a tree and run with gtrace to single step the code to understand it.
The second example will expand on the tree walk and add the type (link as you think) to the leaf item. The the old leaf for something simple like an atom a, will become a new leaf in the tree like (a,atom).
Also this was quickly written as a demonstration only. I am sure it will have problems if pressed into doing anything more than the single example.
:- module(example,
[
example/1
]).
example(walk) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
walk(Term).
example(infer_type) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
infer_type(Term,Is),
write(Is).
walk([]) :- !.
walk([T]) :- var(T), !.
walk(L) :- is_list(L), !, L = [H|T], walk(H), walk(T).
walk(T) :- compound(T), !, T =.. [_|Args], !, walk(Args).
walk(T) :- integer(T), !.
walk(T) :- var(T), !.
walk(T) :- atomic(T), !.
walk(T) :- T =.. [Arg|Args], !, walk(Arg), walk(Args).
infer_type([],[]) :- !.
infer_type([T],[(T,var)]) :- var(T), !.
infer_type(L,S) :- is_list(L), !, L = [H|T], infer_type(H,I), infer_type(T,Is), S = [I|Is].
infer_type(T,S) :- compound(T), !, T =.. [F|Args], !, infer_type(Args,Is), S =.. [F|Is].
infer_type(T,(T,integer)) :- integer(T), !.
infer_type(T,(T,var)) :- var(T), !.
infer_type(T,(T,atom)) :- atomic(T), !.
infer_type(T,S) :- T =.. [Arg|Args], !, infer_type(Arg,I), infer_type(Args,Is), S =.. [I|Is].
Example run
Note: I know there are warnings; it is a demo not production code.
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.3)
?- working_directory(_,'C:/Users/Groot').
true.
?- [example].
Warning: c:/users/Groot/example.pl:20:
Warning: Singleton variables: [T]
Warning: c:/users/Groot/example.pl:24:
Warning: Singleton variables: [T]
true.
?- example(walk).
true.
?- example(infer_type).
term_size(a((1,integer),(Hello,atom),(Atom,atom),(1,integer)+(2,integer),[(a,atom),(($),atom),(_25642,var)]))
true.
As an exercise I did not identify the string as a string, the change should be easy.

Simulating occurs_check=error in SICStus Prolog

It turns out that SICStus Prolog doesn't have an occurs_check
Prolog flag. At least we couldn't find one, and this here
gives an error message:
/* SICStus 4.6.0 (x86_64-win32-nt-4) */
?- set_prolog_flag(occurs_check, true).
Domain error in argument 1 of set_prolog_flag/2
It seems the value "true" is not so much a problem, the
crictical unifications can be realized via the existing
built-in unify_with_occurs_check/2. An interesting value
of an occurs_check Prolog flag is the value "error".
How would one implement a predicate unify_with_occurs_check_and_error/2 ?
Please note, the solution for unify_with_occurs_check_and_error/2
should behave like unify_with_occurs_check/2, i.e. not trigger
attributed variables.
Here is an example usage of the Prolog flag where present:
?- set_prolog_flag(occurs_check, error).
true.
?- X = f(X).
ERROR: ...
And this is what one would do in SICStus Prolog:
?- unify_with_occurs_check_and_error(X, f(X)).
ERROR: ...
Was adapting the code from here and got the following solution:
unify_with_error(X, Y) :- var(X), var(Y), !, X = Y.
unify_with_error(X, Y) :- var(X), !, must_notin(X, Y), X = Y.
unify_with_error(X, Y) :- var(Y), !, must_notin(Y, X), X = Y.
unify_with_error(X, Y) :- functor(X, F, A), functor(Y, G, B),
F/A = G/B,
X =.. [_|L],
Y =.. [_|R],
maplist(unify_with_error, L, R).
must_notin(X, Y) :-
term_variables(Y, L),
maplist(\==(X), L), !.
must_notin(X, Y) :-
throw(error(occurs_check(X, Y),_)).
Seems to work and no interference with attributed variables:
/* SICStus 4.6.0 (x86_64-win32-nt-4) */
?- unify_with_error(X, f(X)).
error(occurs_check(_413,f(_413)),_409)
?- freeze(X, throw(ball)), unify_with_error(X, f(X)).
error(occurs_check(_413,f(_413)),_409)

Pure Prolog Meta-Interpreter with one Rule

I wonder whether there is a pure Prolog meta-interpreter with
only one rule. The usual Prolog vanilla meta-interpreter has two
rules. It reads as follows:
solve(true).
solve((A, B)) :- solve(A), solve(B). /* rule 1 */
solve(H) :- program(H, B), solve(B). /* rule 2 */
This Prolog vanilla meta-interpreter uses two rules /* rule 1 */
and /* rule 2 */. And the rest is facts. The program that
is executed is represented by program facts. Here is an example program:
program(append([], X, X), true).
program(append([X|Y], Z, [X|T]), append(Y, Z, T)).
program(nrev([], []), true).
program(nrev([H|T], R), (nrev(T, S), append(S, [H], R))).
And an example query:
?- solve(nrev([1,2,3], X)).
X = [3, 2, 1] .
Is there a way to represent the program differently as facts, and
then code a different meta-interpreter, which would use only facts
except for a single rule instead of two rules? Something that would
work for all pure Prolog programs, not only the nrev example?
Here is one idea, using a list to hold the rest of the computation:
solve([]).
solve([X|Xs]) :- program(X, Ys, Xs), solve(Ys).
program(true, Xs, Xs).
program(append([],X,X), Xs, Xs).
program(append([X|Y], Z, [X|T]), [append(Y,Z,T)|Xs], Xs).
program(nrev([],[]), Xs, Xs).
program(nrev([H|T],R), [nrev(T,S),append(S,[H],R)|Xs], Xs).
With test call (where one needs to wrap the call in a list).
?- solve([nrev([1,2,3],X)]).
X = [3,2,1] ? ;
no
Arguably, one could represent the program/3 facts as a DCG instead, for increased readability (but then it might not be considered a "fact" any more).
Here is another approach, known as binarization with continuation.
Its from this logic transformers paper here by Paul Tarau (2021).
solve(true).
solve(X) :- program(X, Y), solve(Y).
program(append([],X,X,C), C).
program(append([X|Y],Z,[X|T],C), append(Y,Z,T,C)).
program(nrev([],[],C), C).
program(nrev([H|T],R,C), nrev(T,S,append(S,[H],R,C))).
A little sanity check shows that it wurks:
?- solve(nrev([1,2,3], X, true)).
X = [3, 2, 1] ;
No
If ;/2 is allowed, then this seems to work:
solve(true).
solve(H) :- ((X, Y) = H, solve(X), solve(Y)); (program(H :- B), solve(B)).
program(append([], X, X) :- true).
program(append([X|Y], Z, [X|T]) :- append(Y, Z, T)).
program(nrev([], []) :- true).
program(nrev([H|T], R) :- (nrev(T, S), append(S, [H], R))).
Test:
?- solve(nrev([1,2,3], X)).
X = [3, 2, 1] ;
false.

How to evaluate a variable with string value?

My code does perfect with numbers, but error with single quotation. I'm trying to write a foldl function. When i do foldl1(concat, ['a','b'], X), it reports like "ERROR: Arithmetic: 'ab/0' is not a function". what is the problem? prolog does not allow using is with string?
foldl1(P, [H], X) :-
X is H.
foldl1(P, [H|T], X) :-
foldl1(P, T, Y),
call(P, H, Y, Z),
X is Z.
is/2 evaluates the arithmetic expression to the right, and unifies the result with the term to the left. Unification is also performed against the head' arguments, so you can write a simplified foldl1/3 like
foldl1(_, [H], H).
foldl1(P, [H|T], Z) :-
foldl1(P, T, Y),
call(P, H, Y, Z).
test:
?- foldl1(plus,[1,2,3],R).
R = 6 ;
false.
?- foldl1(concat,[1,2,3],R).
R = '123' ;
false.
I would place a cut after the recursion base, since [H] and [H|T] where T=[] overlap, to avoid any last call - that would anyway fail - on eventual backtracking, like the redo induced by me, inputting ; after the expected first answer while the interpreter waits for my choices.
After the cut (hope you can easily spot where to place it) we get:
?- foldl1(plus,[1,2,3],R).
R = 6.
?- foldl1(concat,[1,2,3],R).
R = '123'.
Now the interpreter 'knows' there are no more answers after the first...
It's also possible to implement a foldl1/3 predicate using first-argument indexing to avoid spurious choice-points without cuts and that is also tail-recursive. From the Logtalk library meta object:
:- meta_predicate(foldl1(3, *, *)).
foldl1(Closure, [Head| Tail], Result) :-
fold_left_(Tail, Closure, Head, Result).
fold_left_([], _, Result, Result).
fold_left_([Arg| Args], Closure, Acc, Result) :-
call(Closure, Acc, Arg, Acc2),
fold_left_(Args, Closure, Acc2, Result).
Sample calls:
?- meta::foldl1(plus,[1,2,3],R).
R = 6.
?- meta::foldl1(concat,[1,2,3],R).
R = '123'.

Prolog: "Vanilla" metainterpreter with builtins

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).

Resources