Undo/1 and !/0

I’am a bit confused with undo/1. I’ve found that it is being executed even if I specify !/0 after it. To my reasoning it shouln’t. Consider this example:
test1 :- writeln("+"), undo(writeln("-")), !.
When I run it I get:

?- test1.
+
true.
-

Why undo goal is executed? How to prevent it from being executed? I want to commit to everything happened before cut.

The undo/1 predicate is called on backtracking, using the same semantics as for unbinding a variable on backtracking. Compare

?- (A = b, fail ; write(A)).
_57118
?- dynamic p/1.
?- (assert(p(1), Ref), undo(erase(Ref)), fail ; \+ p(1))).
true.   % i.e., p(1) is removed

Note that the toplevel REPL loop is failure driven and thus the undo/1 kicks in. You can use ?- set_prolog_flag(toplevel_mode, recursive). to make the toplevel run as a recursive loop. Then you won’t see the undo/1 call.

Possibly, what you expected was this:

myundo(Goal) :-  ( true ; call(Goal) ).

This will as you expected because the !/0 will remove the choice point from ;/2.

2 Likes

Thank you very much for your quick and elaborate answer!

Is undo/1 new? I don’t remember ever encountering this before. (Aside: it would be neat if tiny text on each doc page stated when that was added/last updated.)

commit 2c2acdd29dff4b858317a806452b30f06bd2a97c
Author: Jan Wielemaker <J.Wielemaker@vu.nl>
Date:   Fri May 7 14:26:43 2021 +0200

    ADDED: undo/1 to schedule goals to be called on backtracking.

Surely would be nice. Current commit messages follow stricter rules that might make this more feasible. Still not really easy :frowning:

It might be easier to make a short HOWTO with some ready examples on how to search the commit history assuming that the messages follow the current rules. Maybe such examples would be useful for @abaljeu ?

I took the oxcart route and googled ‘git scm’ then opened the git book. There is a chapter on “Searching” and just scrolling through gave me the incantation:

$ git log -S undo/1

which did find the commit, but it found other stuff too. Either way the whole process took ~60 sec (wall time).

I then thought I would try to figure out how to really do it. First ended up in a dead-end trying to figure out what the -G option does but after some more scrolling and googling ended up with the following:

$ git log --grep='ADDED.*undo'
commit 05a316a92484db2e23779ae658cdab8678527826
Author: Jan Wielemaker <J.Wielemaker@vu.nl>
Date:   Mon May 10 09:26:36 2021 +0200

    ADDED: Define undo/1 as sandbox-safe

commit 2c2acdd29dff4b858317a806452b30f06bd2a97c
Author: Jan Wielemaker <J.Wielemaker@vu.nl>
Date:   Fri May 7 14:26:43 2021 +0200

    ADDED: undo/1 to schedule goals to be called on backtracking.

This process took 4 min wall time.

The docs for git log are here: Git - git-log Documentation

but I admit it was a SO Q/A that did the trick at the end.

PS: this post took me ~15 min wall time.

PPS: I would volunteer to write such a HOWTO (at my own pace :smiley: ) but I would need ideas on what do people really struggle with when it comes to using git in the context of the swipl project. I guess the only SWI-Prolog specific thing is the commit message rules? I know that I have seen the rules described somewhere but now I can’t find them :-/ although it is relatively easy to guess by just looking at the messages. I see ENHANCED, FIXED, DOC, DIST, ADDED but I also see quite a few commit messages that don’t have those. Or was it so that there are no rules but commit messages that start with some set of words are picked up for the release notes?

1 Like

A commit message starting with the regex [A-Z]+: becomes part of the release notes. The set of tags is not fixed, but roughly (might have forgotten some).

  • ENHANCED: Better implementation (faster, less memory, choice points, etc). No change in semantics.
  • FIXED: Fixes some bug
  • CLEANUP: Cleanup of part of the implementation. Should not affect users (but may of course introduce bugs).
  • PORT: Fix build issues on some platform.
  • BUILD: General cross-platform changes to the build process
  • DOC: Changes to the docs.
  • TEST: Modifications to the unit tests
  • MODIFIED: a change that affects user visible behavior that is not a bug fix.

Of course, some are borderline cases and fall in multiple or no category. If there is no such tag this should mean the change is pretty irrelevant. Sometimes no tags are used for commits that prepare for a final commit that is tagged.

Relatively new is that messages where this is relevant also mention the affected predicate. Sometimes as write/1 and friends (i.e., all predicates that uses term serialization).

??? I know my machine is fast, but 4 min vs 0.134 sec is a really big difference (redirect to avoid git using the less pager)

time git log --grep='ADDED.*undo' > /dev/null
real	0m0.134s
user	0m0.126s
sys	0m0.008s

If you have the source, you can also run ./scripts/mkchangelog [version] | less

No I meant literally googling it since I didn’t really know how to do it :sweat_smile: the point was if we know it is possible somehow with git it is relatively easy to figure out how exactly

As a possible reference, Emacs shows a line such as:

Probably introduced at or before Emacs version 29.1.

In the output of describe-function for built-in functions. Looking at the implementation, this is achieved by a search through the NEWS file for the earliest mention of the function’s name.