mthom / scryer-prolog

A modern Prolog implementation written mostly in Rust.
BSD 3-Clause "New" or "Revised" License
2.03k stars 118 forks source link

No easy way to nullify a term using term_expansion without hacks #2623

Open jjtolton opened 2 hours ago

jjtolton commented 2 hours ago

tl;dr

There should be some way to nullify terms in term expansion without needing to result to (:- module(random_name, []), probably by doing user:term_expansion(<pattern>, []).

Overview

There is no easy way to nullify terms using term expansion, and the same is probably true for goal expansion though I haven't tested it yet.

By nullify I mean "expand to zero terms". So to nullify term (b) from the following:

a.
b.
c.

We would expect the nullification of (b) to result in:

a.
c.

Motivation

I like to add the following to my scripts these days:

user:term_expansion((?- Term), (:- module(a, [])).

The way I don't get the following warning in commented queries There should be some way to nullify terms in term expansion without needing to result to (:- module(random_name, []), probably by doing user:term_expansion(<pattern>, []).

:

% Warning: overwriting ?-/1 because the clauses are discontiguous

You can also use the same technique to make custom :- directives:

user:term_expansion((:- setup_table), (:- module(a, []))) :-
             empty_assoc(Assoc),
             bb_put(my_table, Assoc).
:- setup_table.

(this technique is necessary because :- initialization(something) comes after _termexpansion, so if you want to used initialized state, you must initialize the state in the term expansion lifecycle phase rather than then initilization lifecycle phase)

Explanation of Workaround

Note the hack: (:- module(a, []). That's because this is the only way I know of to NULLIFY a term, equivalent to a comment macro in lisp:

(defmacro comment (&rest body) "Comment out one or more s-expressions." nil)

;; this all gets ignore completely when "consulting" the lisp file
(comment
   (foo a b) 
   )

Exploration of Current Behavior

These are the following techniques you would probably expect to work in Prolog:

user:term_expansion((?- Term), _) :- false.

Doesn't work, the term will remain unchanged and then eventually called as is resulting in the same warning. (Note: this is expected, I think. I don't think false should nullify the term).

The following should probably splice or expand into 0 terms in place of the current term:

user:term_expansion((?- Term), []).

however it results in the following warnings:

% Warning: overwriting []/0 because the clauses are discontiguous

which indicate to me that [] is being treated as a fact.

You can get the same warning by doing:


[].

a.

[].

Expectation vs Reality

The net result of this is that you can't make a side-effecting user-defined directive with the :- operator because you get a domain error:

:- womp.

results in

error(domain_error(directive,todo_insert_invalid_term_here),load/1).

unless you do something like:

user:term_expansion((:- womp), (:- module(a, []))) :- 
    <side effects>.

You can't simply do

user:term_expansion((:- womp), _) :- 
     <side-effects>,
     false.

because that will still result in an unchanged (:- womp) which results in

error(domain_error(directive,todo_insert_invalid_term_here),load/1).

Expectation

There should be some way to nullify terms in term expansion without needing to result to (:- module(random_name, []), probably by doing user:term_expansion(<pattern>, []).

triska commented 2 hours ago

You can expand it for example to:

nullified :- false.

Or use for example the predicate name '$nullified' as a more "internal" name that is less likely to conflict with existing predicate names.

hurufu commented 1 hour ago

Nullification works for me:

?- [user].
term_expansion(b, []).
a.
b.
c.

?- a.
   true.
?- b.
   error(existence_error(procedure,b/0),b/0).
?- c.
   true.
?- 
triska commented 1 hour ago

@hurufu: No, it has only replaced b by [] (which currently we cannot call, but it works in rebis-dev!).

You can see the issue with:

term_expansion(b, []).
a.
b.
c.
b.

yielding:

% Warning: overwriting []/0 because the clauses are discontiguous
   true.
hurufu commented 1 hour ago

@hurufu: No, it has only replaced b by [] (which currently we cannot call, but it works in rebis-dev!).

You can see the issue with:

term_expansion(b, []). a. b. c. b. yielding:

% Warning: overwriting []/0 because the clauses are discontiguous true.

Wow, indeed. Silly me. I think it should count as a bug. Then yes, the only solution I see is currently to expand to known "bad" predicate, as you have proposed. Or even better to some undefined dynamic predicate, so it would fail faster.

triska commented 1 hour ago

@hurufu: I think the intuition is justified though, that an expansion of [] results in a vanishing definition, because the definition of the fact [] seems extremely unlikely to be intended.

UWN commented 24 minutes ago

A fact [] can still be written as []:-true.

Note that in SICStus, a single []. is ignored. And a list of terms is just handled as a sequence of such terms.

| ?- [user].
% compiling user...
| [f(1),f(2)].
| 
% compiled user in module user, 44 msec 516288 bytes
yes
| ?- f(X).
X = 1 ? ;
X = 2 ? ;
no

In SWI, however

Welcome to SWI-Prolog (threaded, 64 bits, version 9.3.13)
...
?- [user].
|: [f(1),f(2)].
|: ^D% user://1 compiled 0.00 sec, 4 clauses
true.

?- f(X).
X = 1 ;
X = 2 ;
X = 1, unexpected ;
X = 2, unexpected.