Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DCG body won't parse \+ #602

Closed
dnmfarrell opened this issue Oct 4, 2024 · 13 comments
Closed

DCG body won't parse \+ #602

dnmfarrell opened this issue Oct 4, 2024 · 13 comments

Comments

@dnmfarrell
Copy link

dnmfarrell commented Oct 4, 2024

This DCG matches any list not beginning with a:

% anybuta.pl
anybuta --> \+ "a", any(_).
any(C)  --> [C], any(_).
any([]) --> [].
$ tpl -v
Trealla Prolog (c) Infradig 2020-2024, v2.56.15
$ tpl anybuta.pl
   throw(error(representation_error(dcg_body),[culprit-(\+ [a])])).
?- 

Is this intentional? Is there another way to negate a DCG rule?

@flexoron
Copy link

flexoron commented Oct 4, 2024

I cannot answer your question because I'm not that much into DCG yet.
It might be helpful to see what results you expect from queries.
So I guess things somehow here:

$ cat anybutX.pl
anybutX(X)  --> ({var(X),X="";X=""},!,any(_));{\+var(X)},X,!,{fail}.
anybutX(_)  --> any(_).
any(C)  --> [C], any(_).
any([]) --> [].

$ tpl anybutX.pl
?- anybutX("a","abc",Y).  % bad idea to hard-code, so the first parameter is your "a" but can be anything
   false.

?- anybutX("b","abc",Y). % don't beginn with "b"
   Y = []
;  Y = "c"
;  Y = "bc"
;  Y = "abc".

?- anybutX("abc","abcdef",Y). % "abc"
   false.

?- anybutX("","abc",Y). % "" beginning ignored
   Y = []
;  Y = "c"
;  Y = "bc"
;  Y = "abc".

?- anybutX(X,"abc",Y). % X here a variable so beginning ignored, too 
   X = [], Y = []
;  X = [], Y = "c"
;  X = [], Y = "bc"
;  X = [], Y = "abc".

?- anybutX("abcdef","abc",Y). % Note: "abc" is not matched because beginning should be "abcdef"
   Y = []
;  Y = "c"
;  Y = "bc"
;  Y = "abc".
?- 

@infradig
Copy link
Contributor

infradig commented Oct 4, 2024

This is a dcgs.pl (@UWN) issue as it also present here...

$ scryer-prolog anybuta.pl
   error(representation_error(dcg_body),[culprit-(\+"a")]).
?- 

@infradig
Copy link
Contributor

infradig commented Oct 4, 2024

As per dcgs.pl line 181 negation is implementation defined, and the common implementation does not allow it. SWI-Prolog I believe does.

@flexoron
Copy link

flexoron commented Oct 4, 2024

SWI 9.2.7 accepts the input (\+ "a") but does not answer false which is expected I guess:
?- anybuta([a,b,c],X).
X = [] ;
X = [c] ;
X = [b, c] ;
X = [a, b, c].

Correction: SWI requires list notation:
anybuta --> \+ [a], any(_).
?- anybuta([a,b],Y).
false.
?- anybuta([b,c],Y).
Y = [] ;
Y = [c] ;
Y = [b, c].

@UWN
Copy link

UWN commented Oct 5, 2024

Is this intentional?

Yes it is. There are several issues here. Note that we are talking about the grammar control construct (\+)//1 and the built-in (\+)/1. (Only one slash separates them.)

The first is whether or not this construct should be supported. At first (\+)//1 looks very similar to (\+)/1. However, there is no straight-forward way to use it safely, since the instantiation of its argument is not sufficient to ensure soundness, whereas it is sufficient for (\+)/1.

?- X=3, \+ ( 0 < X, X < 3 ).
   X = 3.
?-     \+ ( 0 < X, X < 3 ), X = 3.
   error(instantiation_error,(is)/2). % safe answer

?- phrase(\+[a],[]).
   error(representation_error(dcg_body),[culprit-(\+"a")])  % conforming Scryer/Trealla
|  true.  % some traditional conforming implementations
?- phrase(\+[a],L), L = [].
   error(representation_error(dcg_body),[culprit-(\+"a")]) % conforming Scryer/Trealla
|  false. % the same traditional conforming implementations

If an implementation now decides to not support this construct, there are two possibilities, only the second is conforming. One is just to provide no translation for (\+)//1 at all. The downside is that this permits the user-programmer to provide an arbitrary user-defined implementation of this construct. Thus, this construct may be different to what some traditional implementations do thereby reducing portability. With a representation error (7.12.2 f) this is prevented. So at least programs written for an implementation like Scryer/Trealla (and that do not fall into this error) will also run in implementations that opted to provide this construct. A representation error does not imply any meaning whereas, say, a type error suggests that there is no solution (but it is still better to produce an error instead of silent failure).

For Scryer/Trealla, that is all you need to know.

And if an implementation decides to support (\+)//1, the next question is how to handle its argument. First, consider here the way how the built-in (\+)/1 is handled:

?- call((fail,1)).
   error(type_error(callable,(fail,1)),(',')/2).
?- call((fail,\+1)).
   false.
?- call((\+1)).
   error(type_error(callable,1),call/1).

So this error is only detected at the point in time of executing the goal. Not earlier on.

In the past, some implementations did convert the argument of (\+)/1 early on. In the meantime, all implementations that provide phrase/2 also conform here except one. See c5.
(\+)//1 is now handled in analogy. Thus the argument is not converted early on.

For all related cases of conformity see #27 and the following.

@dnmfarrell
Copy link
Author

Thanks for the answers everybody, I see now why (\+)//1 is troublesome.

(+)//1 is now handled in analogy

Does this mean there is a way to write a DCG that accomplishes the same thing (e.g. don't match a prefix AND match these other rules), without using (\+)//1 ?

@UWN
Copy link

UWN commented Oct 5, 2024

Does this mean there is a way to write a DCG that accomplishes the same thing (e.g. don't match a prefix AND match these other rules), without using (\+)//1 ?

You can always supply your own definition for a non-terminal. That is, another non-terminal.

@dnmfarrell
Copy link
Author

Thanks @UWN. I think this works:

anybuta --> nota, any.
nota    --> call(eos).
nota    --> [C], { C \= 'a' }.
any     --> [].
any     --> [_], any.

eos([],[]).

@UWN
Copy link

UWN commented Oct 5, 2024

Very close!

?- phrase(anybuta,[C]),C=b.
   false, unexpected
|  instantiation_error % not incorrect, but can be improved
|  C = b. % that would be perfect

See library(reif) for one way to solve this.

@UWN
Copy link

UWN commented Oct 5, 2024

... er, I was a little bit too quick. Just an ordinary dif/2 or dif_si/2 suffices.

@dnmfarrell
Copy link
Author

That is very interesting, thank you! I was wondering why prolog couldn't generate more than one solution for this DCG. E.G:

?- phrase(anybuta,X, []).
X = [] ;
false.

But using dif/2 works!

anybuta --> nota, any.
nota    --> call(eos).
nota    --> [C], { dif(C, a) }.
any     --> [].
any     --> [_], any.

eos([],[]).
?- phrase(anybuta,X, []).
X = [] ;
X = [_392],
dif(_392, a) ;
X = [_404, _410],
dif(_404, a) ;
X = [_416, _422, _428],
dif(_416, a) ;
X = [_428, _434, _440, _446],
...

@flexoron
Copy link

flexoron commented Oct 5, 2024

Excuse me, @dnmfarrell. Hope you don't mind that I mention it again
but it might be helpful to see what results you expect.

GNU Prolog (and SWI and ECLiPSe) accepting both versions of your program:

$ cat anybut.pl
% Version 1
anybut1 --> \+ [a], any(_).
any(C)  --> [C], any(_).
any([]) --> [].
% Version 2
anybut2 --> nota, any.
nota    --> call(eos).
nota    --> [C], { C \= 'a' }.
any     --> [].
any     --> [_], any.
eos([],[]).

$ gprolog --consult-file anybut.pl
| ?- anybut1([b,c],X).
X = [] ? ;
X = [c] ? ;
X = [b,c]

| ?- anybut2([b,c],X).
X = [c] ? ;
X = []

| ?-

Which one?

@dnmfarrell
Copy link
Author

Hey @flexoron, that's very interesting about GNU Prolog, thank you. I was testing with SWI because Trealla didn't support the \+//2 in my DCG. I'm glad to have understood why, and found some better alternatives so I can switch back to Trealla. Thank you @UWN, @infradig.

@infradig infradig added the wontfix This will not be worked on label Oct 6, 2024
@infradig infradig pinned this issue Oct 6, 2024
@infradig infradig removed the wontfix This will not be worked on label Oct 7, 2024
@infradig infradig closed this as not planned Won't fix, can't repro, duplicate, stale Oct 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants