[I7] Avoiding namespace clashes in extensions

I’m building two extensions that define position values:

[code]Notification position is a kind of value. Notification positions are center, top left, top right, bottom left and bottom right.

To show a notification at (pos - notification position):
…[/code]

[code]Tooltip position is a kind of value. Tooltip positions are top left, top right, bottom left and bottom right.

To show a tooltip at (pos - tooltip position):
…[/code]

Each extension works individually but if you include them both they won’t work because value names must be unique.

I can think of a couple of solutions, none of which are optimal:

  • Make the values unique: notification-top-left and tooltip-top-left - I would like to avoid this because the phrases become clunky (“show a notification at notification-top-left”)
  • Use text instead - A bit better, but not much (“show a notification at “top left””). It’d also become possible for the author to give an invalid position.
  • Use phrase options (“show a notification, positioned top left”) - avoids the namespace issue but makes writing and maintaining the extension a pain
  • Use a common value kind both extensions can use (“A position is a kind of value” …) - resolves namespace issues between these two extensions, but the author’s code or some other extension might still clash (for example “center” is a common word).

Any ideas how to best resolve this?

I’d say the last option “use a common value kind both extensions can use” is probably best. You can always change “center” to something else like “middle” or even “centre”!

I wouldn’t use text though, especially as text comparison can be quite fiddly.

Hope this helps.

The best solution would be to fix Inform so that it doesn’t get confused in these situations!

I think a common value would probably be wisest.

Definitely! :laughing:

Some way of restricting the scope of variables will do that quite nicely, especially extension variables.

I suppose that’s the best solution for now.

Tangentially, is there a way to throw a custom run-time programming error if the author uses a value that the extension doesn’t accept?

There sure is! Try this.

[spoiler][code]“Test”

Include (-

Constant RTP_BACKDROP = 1;
Constant RTP_EXITDOOR = 2;
Constant RTP_NOEXIT = 3;
Constant RTP_CANTCHANGE = 4;
Constant RTP_IMPREL = 5;
Constant RTP_RULESTACK = 6;
Constant RTP_TOOMANYRULEBOOKS = 7;
Constant RTP_TOOMANYEVENTS = 8;
Constant RTP_BADPROPERTY = 9;
Constant RTP_UNPROVIDED = 10;
Constant RTP_UNSET = 11;
Constant RTP_TOOMANYACTS = 12;
Constant RTP_CANTABANDON = 13;
Constant RTP_CANTEND = 14;
Constant RTP_CANTMOVENOTHING = 15;
Constant RTP_CANTREMOVENOTHING = 16;
Constant RTP_DIVZERO = 17;
Constant RTP_BADVALUEPROPERTY = 18;
Constant RTP_NOTBACKDROP = 19;
Constant RTP_TABLE_NOCOL = 20;
Constant RTP_TABLE_NOCORR = 21;
Constant RTP_TABLE_NOROW = 22;
Constant RTP_TABLE_NOENTRY = 23;
Constant RTP_TABLE_NOTABLE = 24;
Constant RTP_TABLE_NOMOREBLANKS = 25;
Constant RTP_TABLE_NOROWS = 26;
Constant RTP_TABLE_CANTSORT = 27;
Constant RTP_NOTINAROOM = 28;
Constant RTP_BADTOPIC = 29;
Constant RTP_ROUTELESS = 30;
Constant RTP_PROPOFNOTHING = 31;
Constant RTP_DECIDEONWRONGKIND = 32;
Constant RTP_DECIDEONNOTHING = 33;
Constant RTP_TABLE_CANTSAVE = 34;
Constant RTP_TABLE_WONTFIT = 35;
Constant RTP_TABLE_BADFILE = 36;
Constant RTP_LOWLEVELERROR = 37;
Constant RTP_DONTIGNORETURNSEQUENCE = 38;
Constant RTP_SAYINVALIDSNIPPET = 39;
Constant RTP_SPLICEINVALIDSNIPPET = 40;
Constant RTP_INCLUDEINVALIDSNIPPET = 41;
Constant RTP_LISTWRITERMEMORY = 42;
Constant RTP_CANTREMOVEPLAYER = 43;
Constant RTP_CANTREMOVEDOORS = 44;
Constant RTP_CANTCHANGEOFFSTAGE = 45;
Constant RTP_MSTACKMEMORY = 46;
Constant RTP_TYPECHECK = 47;
Constant RTP_FILEIOERROR = 48;
Constant RTP_HEAPERROR = 49;
Constant RTP_LISTRANGEERROR = 50;
Constant RTP_REGEXPSYNTAXERROR = 51;
Constant RTP_NOGLULXUNICODE = 52;
Constant RTP_BACKDROPONLY = 53;
Constant RTP_NOTTHING = 54;
Constant RTP_SCENEHASNTSTARTED = 55;
Constant RTP_SCENEHASNTENDED = 56;
Constant RTP_NEGATIVEROOT = 57;
Constant RTP_TABLE_CANTRUNTHROUGH = 58;
Constant RTP_CANTITERATE = 59;
Constant RTP_WRONGASSIGNEDKIND = 60;
Constant RTP_SPECIALERROR = 61;

-) instead of “Run-Time Problem Numbers” in “Definitions.i6t”.

Include (-

[ RunTimeProblem n par1 par2 par3 ln i c;
if (enable_rte == false) return;
enable_rte = false;
print “^*** Run-time problem P”, n;
if (ln) print " (at paragraph “, ln, " in the source text)”;
print ": ";
switch(n) {
RTP_BACKDROP:
print "Tried to move ", (the) par1, " (a backdrop) to ", (the) par2,
“, which is not a region.^”;
RTP_CANTCHANGE:
print "Tried to change player to ", (the) par1, “, which is not a person.^”;
RTP_NOEXIT:
print "Tried to change ", (the) par2, " exit of ", (the) par1,
“, but it didn’t seem to have such an exit to change.^”;
RTP_EXITDOOR:
print "Tried to change ", (the) par2, " exit of ", (the) par1,
“, but it led to a door, not a room.^”;
RTP_IMPREL:
print "Tried to access an inappropriate relation for ", (the) par1,
“, violating '”, (string) par2–>RR_DESCRIPTION, “’.^”;
RTP_RULESTACK:
print “Too many procedural rules acting all at once.^”;
RTP_TOOMANYRULEBOOKS:
print “Too many rulebooks in simultaneous use.^”;
RTP_TOOMANYEVENTS:
print “Too many timed events are going on at once.^”;
RTP_BADPROPERTY:
print "Tried to access non-existent property for ", (the) par1, “.^”;
RTP_UNPROVIDED:
print "Since “, (the) par1, " is not allowed the property ~”,
(string) par2, “~, it is against the rules to try to use it.^”;
RTP_UNSET:
print "Although “, (the) par1, " is allowed to have the property ~”,
(string) par2, “~, no value was ever given, so it can’t now be used.^”;
RTP_TOOMANYACTS:
print “Too many activities are going on at once.^”;
RTP_CANTABANDON:
print “Tried to abandon an activity which wasn’t going on.^”;
RTP_CANTEND:
print “Tried to end an activity which wasn’t going on.^”;
RTP_CANTMOVENOTHING:
print “You can’t move nothing.^”;
RTP_CANTREMOVENOTHING:
print “You can’t remove nothing from play.^”;
RTP_DIVZERO:
print “You can’t divide by zero.^”;
RTP_BADVALUEPROPERTY:
print "Tried to access property for a value which didn’t fit: ",
"if this were a number it would be ", par1, “.^”;
RTP_NOTBACKDROP:
print "Tried to move ", (the) par1, " (not a backdrop) to ", (the) par2,
“, which is a region.^”;
RTP_TABLE_NOCOL:
print “Attempt to look up a non-existent column in the table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_NOCORR:
print “Attempt to look up a non-existent correspondence in the table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_NOROW:
print “Attempt to look up a non-existent row in the table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_NOENTRY:
print "Attempt to look up a non-existent entry at column ", par2,
", row “, par3, " of the table '”, (PrintTableName) par1, “’.^”;
RTP_TABLE_NOTABLE:
print "Attempt to blank out a row from a non-existent table (value ",
par1, “).^”;
RTP_TABLE_NOMOREBLANKS:
print “Attempt to choose a blank row in a table with none left: table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_NOROWS:
print “Attempt to choose a random row in an entirely blank table: table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_CANTRUNTHROUGH:
print “Attempt to repeat through a table in a tricky column order: table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_CANTSORT:
print “Attempt to sort a table whose ordering must remain fixed: table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_CANTSAVE:
print “Attempt to save a table to a file whose data is unstable: table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_WONTFIT:
print “File being read has too many rows or columns to fit into table: table '”,
(PrintTableName) par1, “’.^”;
RTP_TABLE_BADFILE:
print “File being read is not a previously saved table: table '”,
(PrintTableName) par1, “’.^”;
RTP_NOTINAROOM:
print “Attempt to test if the current location is '”,
(the) par1, “’, which is not a room or region.^”;
RTP_BADTOPIC:
print “Attempt to see if a snippet of text matches something which
is not a topic.^”;
RTP_ROUTELESS:
print “Attempt to find route or count steps through an implicit
relation.^”;
RTP_PROPOFNOTHING:
print "Attempt to use a property of the ‘nothing’ non-object: property ",
(PrintPropertyName) par2, “^”;
RTP_DECIDEONWRONGKIND:
print “Attempt to ‘decide on V’ where V is the wrong kind of object.^”;
RTP_DECIDEONNOTHING:
print “Attempt to ‘decide on nothing’.^”;
RTP_LOWLEVELERROR:
print “Low level error.^”;
RTP_DONTIGNORETURNSEQUENCE:
print “Attempt to ignore the turn sequence rules.^”;
RTP_SAYINVALIDSNIPPET:
print "Attempt to say a snippet value which is currently invalid: words ",
par1, " to ", par2, “.^”;
RTP_SPLICEINVALIDSNIPPET:
print "Attempt to splice a snippet value which is currently invalid: words ",
par1, " to ", par2, “.^”;
RTP_INCLUDEINVALIDSNIPPET:
print "Attempt to match a snippet value which is currently invalid: words ",
par1, " to ", par2, “.^”;
RTP_LISTWRITERMEMORY:
print “The list-writer has run out of memory.^”;
RTP_CANTREMOVEPLAYER:
print “Attempt to remove the player from play.^”;
RTP_CANTREMOVEDOORS:
print “Attempt to remove a door from play.^”;
RTP_CANTCHANGEOFFSTAGE:
print “Attempt to change the player to a person off-stage.^”;
RTP_MSTACKMEMORY:
print “The memory stack is exhausted.^”;
RTP_TYPECHECK:
print “Phrase applied to an incompatible kind of value.^”;
RTP_FILEIOERROR:
print “Error handling external file.^”;
RTP_HEAPERROR:
print “Memory allocation proved impossible.^”;
RTP_LISTRANGEERROR:
print “Attempt to use list item which does not exist.^”;
RTP_REGEXPSYNTAXERROR:
print “Syntax error in regular expression.^”;
RTP_NOGLULXUNICODE:
print “This interpreter does not support Unicode.^”;
RTP_BACKDROPONLY:
print “Only backdrops can be moved to multiple places.^”;
RTP_NOTBACKDROP:
print "Tried to move ", (the) par1, " (not a thing) to ", (the) par2,
“, but only things can move around.^”;
RTP_SCENEHASNTSTARTED:
print "The scene “, (PrintSceneName) par1,
" hasn’t started, so you can’t ask when it did.^”;
RTP_SCENEHASNTENDED:
print "The scene “, (PrintSceneName) par1,
" hasn’t ended, so you can’t ask when it did.^”;
RTP_NEGATIVEROOT:
print “You can’t take the square root of a negative number.^”;
RTP_CANTITERATE:
print "You can’t implicitly repeat through the values of this kind: ",
“a problem arising from a description which started out here - ~”,
(string) par1, “~.^”;
RTP_WRONGASSIGNEDKIND:
print "Attempt to set a variable to the wrong kind of object: ",
“you wrote '”, (string) par2, "’, which sets the value to “, (the) par1,
" - but that doesn’t have the kind '”, (string) par3, “’.^”;
RTP_SPECIALERROR:
print “You can’t do that.^”;
}
print “^”;
];

-) instead of “Reporting” in “RTP.i6t”.

To give a run time error: (- RunTimeProblem(RTP_SPECIALERROR); -).

Every turn: give a run time error.

The Testing Room is A Room.[/code][/spoiler]

However, the fancy Inform 7 user interface messages need to be done separately in HTML and put in the right location.

Hope this helps.

It would be awesome if Inform had extension-based namespaces. Then we wouldn’t have anywhere near as many extension conflicts.

Thanks, I’ll keep that in mind. For most errors it might be better just to fake it.

Use the parenthetical section headings (for use with/without ToolTips by Juhana) so the extensions can know when they’re being used with each other and when they’re not. It’s in the manual somewhere.

Custom run-time errors is an open feature request. There’s not much detail, though – feel free to add the real-life cases you’re running into.

inform7.uservoice.com/forums/573 … e-problems

Namespacing is also a feature request, but perhaps in limbo due to “fewer complaints about namespace clashes recently”. It may be worth giving that one a kick too. (I am always in favor of namespacing.)

While this could be very useful, wouldn’t you need to create a HTML page for the Inform 7 user interface to got with it and make sure that it is located in the right folder? Otherwise, you’d get a “This program cannot display the webpage” message rather than the I7 run time error explanation page.

Once we understand the problem clearly, we can solve it.