So, what’s this new feature I want?
Well, imagine some code like this.
(give the tutorial)
(div @tutorial) {
Useful actions might include: (line)
(exhaust) { *(useful action) (line) }
}
(useful action)
(current room $Room) *($Obj is #in $Room) (item $Obj)
TAKE something from the ground
(useful action)
*($Item is #heldby #player) ~($Item = #compass)
DROP something from your inventory
(useful action)
*($Item is #heldby #player) ~($Item = #compass)
(current room $Room) *($Obj is #in $Room) (supporter $Obj)
PUT something ON something
Seems straightforward enough, right? Until…
Useful actions might include:
TAKE something from the ground
TAKE something from the ground
DROP something from your inventory
DROP something from your inventory
DROP something from your inventory
PUT something ON something
PUT something ON something
PUT something ON something
PUT something ON something
PUT something ON something
PUT something ON something
Oops! Each of those multi-queries created a choice point, and the (exhaust) ran through all of those choice points multiple times. This is the trap I fell into with Miss Gosling.
It’s not limited to (exhaust), though. This is a problem that Linus noticed in early Dialog, which is why (if) exists. The structure (if) COND (then) TRUE (else) FALSE (endif) is almost equivalent to { COND TRUE (or) FALSE }. But as described in the manual:
There are subtle differences between the if-statement above and the disjunction shown earlier: An if-condition is evaluated at most once, even if it creates choice points. […] In the disjunction-based version of the rule, there are several lingering choice points, so if a failure is encountered further down in the rule (or even in the calling rule, if this was a multi-query), then execution might resume somewhere in the middle of this code, printing half-sentences as a result.
This means you can use an (if) block to throw away choice points:
(if)
ARBITRARY CODE
ARBITRARY CODE
(then) (else) (fail) (endif)
And I used this extensively in the Scott Adams transpiler when I didn’t want to leave choice points around:
(if)
%% SECURITY BADGE
(here #picture-of-me-stamped-security)
(now) (#picture-of-me-stamped-security is at #grey-room)
(go to #sitting)
There's a Bright flash & I hear something fall to the floor. (line)
I can't see what it is from here though. (line)
(line)
(then) (endif)
But it’s clearly a hack, and it’s not obvious at a glance why I’m doing this. Why is the (then) branch empty?
You can also do this:
(style class @default)
(span @default) { ARBITRARY CODE ARBITRARY CODE }
Divs and spans can succeed at most once; after the div or span exits, any choice points left inside are discarded. (This makes sure that all the reset-the-style code doesn’t run multiple times.)
But again, this is clearly a hack. The purpose of divs and spans is to style the document, and it’s not at all obvious that removing this seemingly-pointless span will change the behavior of the code.
The best solution currently, and the one that I went with in Miss Gosling, is to make additional predicates.
(useful action)
(droppable item in inventory and supporter in room)
PUT something ON something
(droppable item in inventory and supporter in room)
*($Item is #heldby #player) ~($Item = #compass)
(current room $Room) *($Obj is #in $Room) (supporter $Obj)
A simple query (that is, not a multi-query) is the easiest way to get rid of choice points. But this leads to a lot of extra predicates, and it moves the code away from where it logically belongs. In my opinion, this works, but it’s not elegant.
So, what can we do about this?
In 1c/01, I would like to add an additional bit of syntax:
(at most once) { ARBITRARY CODE ARBITRARY CODE }
This would use the same mechanism currently used for (if), (div $), and (span $) to discard all choice points from the block once it exits. (Specifically, it would store the value of CHO (the choice point register) to a temporary variable before starting the block, then restore it afterward.) That mechanism has been around for a long time already and is well-tested; this would just give authors a clearer way of accessing it.
I have a significant desire for this feature, so I’m specifically not going to make a poll for it; even if the forum is indifferent, it would be very helpful to me in multiple contexts. But if you have strong objections to this, please do let me know! And if you don’t like the (at most once) name, also let me know on that front; I don’t care too much about the exact syntax. I just want it to be clearer at a glance than (span @default).