"printing the name" rule side effects happen 2x? [resolved]

I just ran into this quirk, and wonder if anyone who understands the underlying I6 better than I do can explain whether it’s a “bug” or a “feature” :wink: … and more importantly, if there is a way to get around it. What happens in a nutshell is that “printing the name” rules are firing, not once, but twice whenever an object name is being printed up with either the definite or indefinite article. (Proper-named things don’t appear to be affected.) This means in particular that any printing the name rules with side effects can and do produce unexpected results.

If you find this hard to believe – believe me, so did I – please read on, because you can’t detect the double-invocation simply by whatever a “printing the name” rule prints up. Evidently one of these two passes doesn’t actually print any output! However it’s quite easy to verify that the rules are being hit twice, and not just once.

The following I7 code demonstrates the behavior:

[code]“Print Rule Quirk Demo” by Taryn Michelle

[Demonstrating that some I6 print rules trigger ‘printing the name of’ activity 2x per object printed!]

[First, a place to track the times we hit a rule for an object]
A rule-counter is a kind of object.
A rule-counter has a number called before-count. before-count of a rule-counter is usually 0.
A rule-counter has a number called for-count. for-count of a rule-counter is usually 0.
A rule-counter has a number called after-count. after-count of a rule-counter is usually 0.

A thing has a rule-counter called print-counter.
A thing has a number called the examine-count. The examine-count of a thing is usually 0.

[Now let’s check how many times ‘printing the name’ rules are hit for each object; also, we’re going to see from the results that the --> <-- we put around a printed object name appear only once EVEN IN CASES WHERE THIS RULE FIRES TWICE PER PRINT of the object. This illustrates why it was not possible to detect the “double-rule-activation” simply by adding debug output to the rules. We need to count hits to show what’s happening.]
before printing the name of something (called the target):
say “–>”;
if the print-counter of the target is not nothing:
increase before-count of the print-counter of the target by 1.

for printing the name of something (called the target):
if the print-counter of the target is not nothing:
increase for-count of the print-counter of the target by 1;
make no decision.

after printing the name of something (called the target):
say “<–”;
if the print-counter of the target is not nothing:
increase after-count of the print-counter of the target by 1.

[Try calling one of the print rules directly - to confirm it has the same behavior as when the underling inform does “print () obj.” ]
To say special-the (item - a thing): (- DefArt({item}); -).

the Chamber of Proof is a room. “[chamber-description]”;

To say chamber-description:
say “Wherein we shall demonstrate [the awful quirkiness] implicit in I6’s underlying use of ‘print rules’…[line break][an articulate thing] is double-counted when ‘said’ by the normal rules – these translate in I6 into something like ‘print (a) obj’; [paragraph break]”;
say “whereas [inarticulated] (said with no article, definite or indefinite) is not – the I6 translation here is ‘print (name) obj’. [paragraph break]”;
say “Finally, taking [special-the direct approach] we print the article by calling the print rule directly using ‘DefArt/IndefArt (obj)’ instead of ‘print (a)/(the) obj’.[paragraph break]”;
say “We use --> <-- to mark printing the name of objects (1) to make it clear to see how many times they’ve actually been printed in the text, but also (2) to demonstrate, despite triggering the ‘printing the name’ rules twice, while the ‘side effects’ happen every time (i.e., double-counting in our example), anything actually printed from within the rules only occurs once, thus internally output must somehow be suppressed the other time. [paragraph break]”;
say “Lastly, the objects printed out below by the ‘listing nondescript items’ activity again manifest the same behavior, which is unsurprising, since the underlying I6 list-printing code also ends up at some form of ‘print () obj;’[line break]”;

[Now scatter some things about to be printed in various ways]
the printery is a thing in the Chamber of Proof.
printery-counter is a rule-counter. the print-counter of the printery is printery-counter.

in the chamber is a thing called The Decidedly Proper Thing.
proper-counter is a rule-counter. the print-counter of the proper thing is proper-counter.

In the chamber is a thing called an articulate thing. The articulate thing is scenery.
articulate-counter is a rule-counter. the print-counter of the articulate thing is articulate-counter.

In the chamber is a thing called something inarticulated. something inarticulated is scenery.
inarticulate-counter is a rule-counter. the print-counter of something inarticulated is inarticulate-counter.

the awful quirkiness is scenery in the chamber.
quirkiness-counter is a rule-counter. the print-counter of the awful quirkiness is quirkiness-counter.

the direct approach is scenery in the chamber.
direct-counter is a rule-counter. the print-counter of the direct approach is direct-counter.

[And now some output to illustrate what’s going on. After every “look” or “examine” most of the objects printed up will have triggered their “printing the name” rules, not once, but twice. Only objects that are proper-named – i.e., which under the covers are firing off the (name) rule, and not one of the rules to print an article – have the normal count we would have expected (that is, once per time displayed).]

look-count is a number that varies. look-count is 0.

To say rule count of (target - a thing):
say “<[printed name of target]>”;
if the print-counter of target is nothing:
say " does not support ‘printing the name’ rule counting.";
stop;
say “has been looked at [look-count] time(s) and examined [examine-count of the target] times. It has triggered ‘printing the name’ rules [before-count of print-counter of target], [for-count of print-counter of target] and [after-count of print-counter of target] times (before, for and after)”;

after looking:
increase look-count by 1;
repeat with item running through things in the chamber:
say “[rule count of item].”;

after examining something (called the item):
increase the examine-count of the item by 1;
say “[rule count of item].”;[/code]

I’m using what I think is the latest build, 6G60. If you’ve got any clue what’s going on, thanks in advance for taking the time to share!

– Taryn

Okay…while waiting for my post to be moderated… (That’s not a complaint, by the way – I know we all have lives outside of “IF” – and all efforts to support these forums are greatly appreciated!!!)

I dug deeper and saw the issue, inside the guts of Printing.i6t. To determine what article to print, the routine PrefaceByArticle first does a pass where it prints to an array buffer. Nothing gets displayed during this pass (as observed) but otherwise, all printing the name rules fire as normally (as they must, in case something gets changed that would affect the article needing to be output).

Why the routine doesn’t then use this buffer itself to display the output, I’m not sure. I’m still learning (by necessity, as I go) the gory details of I6 – I poked around for awhile and I could not figure out how to do it, so maybe it’s not possible?? Doesn’t make a great deal of sense to me…so that’s the new question. Could that buffered output just be printed directly without making a second call to PSN__ (which triggers the second invocation of the printing a name activity)?

Absent that, here’s new code to work around the issue by setting a flag within the PrefaceByArticle routine; an activity variable is added to “printing the name” to determine when the rule is being invoked because of “article-choosing”. This at least allows any “printing the name” rules that have side effects to test whether or not to run those side effects (while article-choosing, no, otherwise yes).

The revised code is below (with a small bit of clean-up of the earlier demo code as well):

[code]“Print Rule Quirk Demo” by Taryn Michelle

[Demonstrating that some I6 print rules trigger ‘printing the name of’ activity 2x per object printed!]

[First, a place to track the times we hit a rule for an object]
A rule-counter is a kind of object.
A rule-counter has a number called before-count. before-count of a rule-counter is usually 0.
A rule-counter has a number called for-count. for-count of a rule-counter is usually 0.
A rule-counter has a number called after-count. after-count of a rule-counter is usually 0.

null-counter is a rule-counter.

A thing has a rule-counter called print-counter. The print-counter of a thing is usually null-counter.
A thing has a number called the examine-count. The examine-count of a thing is usually 0.

[Now let’s check how many times ‘printing the name’ rules are hit for each object; also, we’re going to see from the results that the --> <-- we put around a printed object name appear only once EVEN IN CASES WHERE THIS RULE FIRES TWICE PER PRINT of the object. This illustrates why it was not possible to detect the “double-rule-activation” simply by adding debug output to the rules. We need to count hits to show what’s happening.]
before printing the name of something (called the target):
if article-choosing is false:
say “–>”;
increase before-count of the print-counter of the target by 1.

for printing the name of something (called the target):
if article-choosing is false:
increase for-count of the print-counter of the target by 1;
make no decision.

after printing the name of something (called the target):
if article-choosing is false:
say “<–”;
increase after-count of the print-counter of the target by 1.

[Try calling one of the print rules directly - to confirm it has the same behavior as when the underling inform does “print () obj.” ]
To say special-the (item - a thing): (- DefArt({item}); -).

the Chamber of Proof is a room. “[chamber-description]”;

To say chamber-description:
say “Wherein we shall demonstrate [the awful quirkiness] implicit in I6’s underlying use of ‘print rules’…[line break][an articulate thing] is double-counted when ‘said’ by the normal rules – these translate in I6 into something like ‘print (a) obj’; [paragraph break]”;
say “whereas [inarticulated] (said with no article, definite or indefinite) is not – the I6 translation here is ‘print (name) obj’. [paragraph break]”;
say “Finally, taking [special-the direct approach] we print the article by calling the print rule directly using ‘DefArt/IndefArt (obj)’, just to confirm that this behaves the same as print (the)/(a) obj;’ i.e., the rules are invoked two times instead of one.[paragraph break]”;
say “We use --> <-- to mark printing the name of objects (1) to make it easy to spot where they’ve actually been printed in the text, but also (2) to demonstrate, despite triggering the ‘printing the name’ rules twice, while the ‘side effects’ happen every time (i.e., double-counting in our example), anything actually printed from within the rules only occurs once. We get only one set of ‘–>’ and ‘<–’ printed, not two. Thus internally, printed output must somehow be suppressed one of the two times we pass through each rule. [paragraph break]”;
say “Lastly, the objects printed out below by the ‘listing nondescript items’ activity again manifest the same behavior, which is unsurprising, since the underlying I6 list-printing code also ends up at some form of ‘print () obj;’[line break]”;

[Now scatter some things about to be printed in various ways]
the printery is a thing in the Chamber of Proof.
printery-counter is a rule-counter. the print-counter of the printery is printery-counter.

in the chamber is a thing called The Decidedly Proper Thing.
proper-counter is a rule-counter. the print-counter of the proper thing is proper-counter.

In the chamber is a thing called an articulate thing. The articulate thing is scenery.
articulate-counter is a rule-counter. the print-counter of the articulate thing is articulate-counter.

In the chamber is a thing called something inarticulated. something inarticulated is scenery.
inarticulate-counter is a rule-counter. the print-counter of something inarticulated is inarticulate-counter.

the awful quirkiness is scenery in the chamber.
quirkiness-counter is a rule-counter. the print-counter of the awful quirkiness is quirkiness-counter.

the direct approach is scenery in the chamber.
direct-counter is a rule-counter. the print-counter of the direct approach is direct-counter.

[And now some output to illustrate what’s going on. After every “look” or “examine” most of the objects printed up will have triggered their “printing the name” rules, not once, but twice. Only objects that are proper-named – i.e., which under the covers are firing off the (name) rule, and not one of the rules to print an article – have the normal count we would have expected (that is, once per time displayed).]

look-count is a number that varies. look-count is 0.

To say rule count of (target - a thing):
say “<[printed name of target]>”; [use printed name so as not to invoke ‘printing the name’ rules here]
if the print-counter of target is null-counter:
say " does not support ‘printing the name’ rule counting";
otherwise:
say “has been looked at [look-count] time(s) and examined [examine-count of the target] times. It has triggered ‘printing the name’ rules [before-count of print-counter of target], [for-count of print-counter of target] and [after-count of print-counter of target] times (before, for and after)”;

after looking:
increase look-count by 1;
repeat with item running through things in the chamber:
say “[rule count of item].”;

after examining something (called the item):
increase the examine-count of the item by 1;
say “[rule count of item].”;

test me with “look / x something inarticulated / x proper thing / x quirkiness / x direct approach”.

Section - Here’s a Workaround

Include (-
[ PrefaceByArticle obj acode pluralise capitalise i artform findout artval;
article_choosing = false; ! flag to signal printing the name activity when printing to array for article determination
if (obj provides articles) {
artval=(obj.&articles)–>(acode+short_name_case*LanguageCases);
if (capitalise)
print (Cap) artval, " ";
else
print (string) artval, " ";
if (pluralise) return;
print (PSN__) obj; return;
}

i = GetGNAOfObject(obj);
if (pluralise) {
	if (i < 3 || (i >= 6 && i < 9)) i = i + 3;
}
i = LanguageGNAsToArticles-->i;

artform = LanguageArticles
	+ 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases);

#Iftrue (LanguageContractionForms == 2);
if (artform-->acode ~= artform-->(acode+3)) findout = true;
#Endif; ! LanguageContractionForms
#Iftrue (LanguageContractionForms == 3);
if (artform-->acode ~= artform-->(acode+3)) findout = true;
if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
#Endif; ! LanguageContractionForms
#Iftrue (LanguageContractionForms == 4);
if (artform-->acode ~= artform-->(acode+3)) findout = true;
if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true;
if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true;
#Endif; ! LanguageContractionForms
#Iftrue (LanguageContractionForms > 4);
findout = true;
#Endif; ! LanguageContractionForms

#Ifdef TARGET_ZCODE;
if (standard_interpreter ~= 0 && findout) {
	StorageForShortName-->0 = 160;
	@output_stream 3 StorageForShortName;
	article_choosing = true;
	if (pluralise) print (number) pluralise; else print (PSN__) obj;
	article_choosing = false;
	@output_stream -3;
	acode = acode + 3*LanguageContraction(StorageForShortName + 2);
}
#Ifnot; ! TARGET_GLULX
if (findout) {
	article_choosing = true;
	if (pluralise)
		Glulx_PrintAnyToArray(StorageForShortName, 160, EnglishNumber, pluralise);
	else
		Glulx_PrintAnyToArray(StorageForShortName, 160, PSN__, obj);
	article_choosing = false;
	acode = acode + 3*LanguageContraction(StorageForShortName);
}
#Endif; ! TARGET_

Cap (artform-->acode, ~~capitalise); ! print article
if (pluralise) return;
! Shouldn't there be some way to just output the buffered result we obtained above? One would think, but I have yet to find it...thus the article_choosing flag approach implemented above.
print (PSN__) obj;

]; -) instead of “Object Names II” in “Printing.i6t”;

Include (- Global short_name_case; -) before “Object Names II” in “Printing.i6t”;

Include (- Global article_choosing; -) after “Definitions.i6t”;

To decide whether name-printing is choosing articles: (- ( article_choosing ) -).

The printing the name activity has a truth state called article-choosing.

First before printing the name:
if name-printing is choosing articles:
now article-choosing is true;
otherwise:
now article-choosing is false;
[/code]

Once again, if anyone who knows the guts of I6, the Z-Machine and Glulx can offer a more straightforward solution, that would be appreciated!

Wow. I had never noticed that, and it might explain certain bugs in my code. The I6 workaround is much appreciated!

A cursory look suggests that it could except for two concerns: (1) StorageForShortName uses a fixed-size buffer, so the text might be cut off. (2) PrefaceByArticle needs to be reentrant. (That bug causes problems for your code too—a second inner PSN__ call will believe it is actually printing, even if it is in fact part of an outer PSN__ call to determine an article. Combine your changes with this patch, and you should be okay.)

Of course, in most stories names will never overflow the buffer, nor will PrefaceByArticle calls nest, so these are really only edge cases.

Double edit: My brain is not working today; please ignore what I had written before.

Thanks for looking into that! I’d known that printing the article ran the rules for printing, and it had been a source of some frustration, but I hadn’t known anything about the internals.

Thanks for pointing this out! It’s those niggling little edge cases that often end up causing us a ton of time and frustration once we run into them – which generally happens sooner or later!

As far as printing the array buffer, you’re right, the fixed size alone makes the solution “fragile”. On further consideration, allowing the two PSN__ calls to take place as-is, and simply detecting which is which seems the preferable solution in any case. Printing anything in a before rule, for example, can change the article that Inform detects should be used, which might be right or wrong depending on the circumstance. (In my sample code, placing → ← around the printed object names ended up causing Inform to say “a → articulated thing” instead of “an”, for example.) Careful use of the test for “article-choosing” to decide what to print and when resolves this kind of thing, where a single PSN__ pass would not be able to.

At this point, I’m inclined to package up your patch (the one you note above, dealing with reentrance) along with this tweak and try submitting it as a small extension. It’s probably not a fix that every author is going to find they need, but I feel like it would be helpful even just in terms of documenting the way printing (really) works, and getting that bit of documentation out on the main Inform 7 website.

I’m going to package it that way (as an extension) for my own use in any case, so if anyone is interested in giving the extension a test drive, so to speak, let me know and I’ll be glad to share. I’ve written extensions for my own use before, but not published any, and I imagine more pairs of eyes giving it a look prior to my trying to submit anything couldn’t hurt :wink:

Cool. Thanks!

Then let me plug and invite you to Friends of I7, a way that a number of us share I7 code, and in particular the extensions project. I think the current protocol for joining is to grab a Github account and PM Dannii. (Dannii, correct me if I’m wrong.)

Thanks, eu, for the suggestion (and to Dannii for adding me) – I’ve just uploaded the extension (Print Stage Detection) to the Friends of I7/extensions project. If/when you get a chance to eyeball it, let me know what you think.

I’m hoping this extension can help me solve a little dilemma I asked about in another thread. When I found it, it seemed like it would. I’m trying to find a way to insert rules before the article is printed… in the case of this extension, before the article-choosing stage. I tried this (a silly use-case just to demonstrate, obviously a real use would be more in-depth and useful):

Include Print Stage Detection by Taryn Michelle.

Before printing the name of a thing (called the target):
	if the print-stage is article-choosing:
		now the target is proper-named;

The kitchen is a room.

The player is in the kitchen.

An apple is in the kitchen.

My hope would be that the apple would end up without the “an”, since it is proper-named now. However, this only works after the second time the apple is examined, meaning that the before rule is still not actually running before the article is chosen. Is there something I’m doing wrong?