Metainterpreter puzzler with `member/2`

I’m using: SWI-Prolog version 8.0.1, but I see the same problem in 8.0.2 and 7.2.3.

I’m using the simple metainterpreter at my S.O. question:

solve(true) :- !.
solve((A,B)) :- !, solve(A), solve(B).
solve(H) :- clause(H,B), solve(B).
solve(T) :- call(T).

When I run this query the first time, it works fine:

?- solve(member(X, [elizabeth,socrates])).
X = elizabeth ;
X = socrates.

The second time, however, it gives me this:

?- solve(member(X, [elizabeth,socrates])).
ERROR: Undefined procedure: member_/3
ERROR:   However, there are definitions for:
ERROR:         member/2
ERROR: 
ERROR: In:
ERROR:   [10] member_([socrates],_2556,elizabeth)
ERROR:    [9] solve(member_([socrates],_2594,elizabeth)) at /users/dlyons/test.pl:15
ERROR:    [8] solve(member(_2630,[elizabeth|...])) at /users/dlyons/test.pl:14
ERROR:    [7] <user>
   Exception: (10) member_([socrates], _1808, elizabeth) ? abort

You can induce the second-case behavior by doing [library(lists)] at the start. Is this a problem with my metainterpreter? If so, what should I do to fix it?

You are seeing a rather odd behaviour. In the initial call there are no clauses for member/2, so you call it. That traps the autoloader, creating clauses for it (and running them). Next time the clause call works as clauses have been added, but the emulation needs to be aware of modules and you simple one is not.

What can I do to improve this metainterpreter? It has another shortcoming as well, that you get multiple solutions because the behavior of call/1 includes the behavior using clause/2.

Is this part of the problem? (in library(lists)):

:- set_prolog_flag(generate_debug_info, false).

I don’t think so. What Jan is saying is that on the first call, there are no clauses for me to pick up with clause/2 so it goes into call/1 which invokes the autoloader and then successfully calls it (as a builtin). On my second attempt, there are clauses to pick up with clause/2 (the autoloader dragged them in on the previous attempt), but when I subsequently use call/1 on the body of the clause, I’m failing to take into account the module system somehow and those calls fail.

The first problem thus is that I don’t know how to make this work with modules. The second problem is that, even if I do, I will get spurious results when I enter my “last resort” of just using call/1. And the third problem is that, for the purposes of this S.O. answer I probably don’t want to disassemble predicates not supplied by the user anyhow, if there is a convenient way to distinguish “system” predicates from “user” predicates.

The simplest solution is probably to use as condition

\+ predicate_property(Goal, imported_from(_)).

A full meta interpreter that deals with the module system has to keep track of the module context, use the above as well as predicate_property(Goal, meta_predicate(Spec)) to properly resolve the module context for the arguments of meta-predicates. And … deal with cuts, etc.

This is all doable, but pretty complex. Meta interpreters are nice for tutorial purposes. You rarely find them in real life. In cases you would need them the rules are typically represented as facts rather than Prolog clauses.

1 Like

Thank you for the help! I am wondering if you could elaborate on what you mean by meta-interpreter rules being represented as facts rather than clauses.

1 Like

There is not that much point in meta-interpreting Prolog. Even for debugging reasons there are other ways such as hooking into the debugger or use source code rewriting to wrap goals whose execution you want to track. You may want to use a Prolog-like rule language for some domain and use rule interpretation such that you can explain which rules have been used to get to some conclusion.

In that case you typically design a syntax that fits your need, write down the rules using this syntax and interpret these rules. The interpretation may look at lot like a Prolog meta-interpreter, but you probably
have no cuts, modules and other complications. Prolog dynamic operators allow you to easily invent a rule syntax that should suit your domain.

I’m not much into rule based systems so I do not have an example right here. I did once write one for recognising typical (near) alignments of elements in diagrams.