[I7] Limits of extending I7

Hey everyone,

I know that the open-sourcing of I7 is apt to take time, and I appreciate that it’s not easy making it ready. I know equally well that the workings of NI is largely a mystery. Still, we know a little about preform, and we have some idea of what goes on with the syntax parsing. Therefore, thought I’d speculate about possible features, sort of test the waters of what users would like in the future in the way of where I7 should go.

For myself, I have two things I’d like to see. First would be a way to easily negate relations. We can already invert the terms, but I don’t know of an easy way to simply add a “not” verb.

Relevancy relates a thing (called x) to a thing (called y) when x can see y. The verb to be relevant to means relevancy. The verb to be irrelevant to means the negated relevancy relation.

Secondly, I’d like to see a way to merge relations and rules, sort of a relational rule:


A thing is usually relevant to the player.
A thing is usually irrelevant to a not player person.
An off-stage thing is usually irrelevant to the player.
...

Would this be useful to anyone else? Is there a reason why this would be a bad thing, or ill-considered, or difficult to implement? Are there other features you would want to have in a future iteration of I7?

You’ve made one suggestion about extended syntax, and one about extending the underlying mechanisms by which relations are implemented.

Just want to be clear that these are different scopes. :)

Is the idea that we are just tossing out wishes here? If so, I’m game. Things I would love to see in the next (or some future) release (or hope that someone will quickly point out are already included or are easy to implement), fully understanding that they may be vastly more complicated or difficult than I as a non-programmer appreciate:

  • Ternary (or larger) relations – Relations are wonderful and amazing things to have be in the vocabulary of the language. (Truly!) Three-value relations would make some situations easier to deal with. I imagine (probably incorrectly) that the major issue is not so much the technical implementation of the mechanics so much as a suitably English-like syntax for declaring, asserting, and building conditions with them. Given that, functionally speaking, a stored action is a four-value relation, this does not seem insurmountable (though I recognize that that’s a specific case, not the general case). SQL provides a structural template for a way that a general language of such conditions might be modeled.

  • Definition of semantics of Standard Rule action success/failure – The action model describes actions as succeeding or failing, but there does not appear to be a published definition of what constitutes success or failure for each Standard Rule action, nor is the implementation of success or failure in Standard Rule actions entirely consistent with the text presented to the player. (Some of them retain the structural imprint of their original translation from the I6 Standard Library 6/10, in which success and failure of actions in the I7 sense has no proper analog.) As an example, the block showing rule registers a failure of the action but displays something like “Bob is unimpressed.” Did the player fail to show it because Bob was unimpressed, or did it simply not generate a story-significant state change? Should the condition “if we have shown the trinket to Bob…” evaluate false just because Bob was not impressed? I understand that different authors may prefer different semantics here, but listing Standard Rule assumptions would be helpful to new authors. It might also encourage more use of the built-in action success/failure tracking if it worked consistently with naive expectations. [EDIT: Regarding this idea – a first draft has been created. See Semantics of Standard Rules Actions in Inform 7 6M62 for details.]

  • Negation of “decide whether” phrases – You mentioned negating relations, but it would also be nice to be able to negate To decide whether... phrases. Something structurally similar to the rather than syntax put in place for definitions might be applicable here, perhaps “versus” as in “To decide whether all is well versus things look grim:...

  • A “number of turns that” phrase – With all the machinery built around for the first time, for six turns, and so on, it would be nice to have a phrase that evaluates to the number of turns in a row that a condition has been true or false. (say "You've already waiting here for [number of turns that the player has been in Waiting Room] minutes!")

2 Likes

This was requested at the old feature request site and the response was that it would indeed be infeasible technically with the relation system that’s currently in place.

The most formidable problems are to do with the storage requirements – naive users could easily ask for enormous amounts of storage (e.g., a ternary relation between things) without realising how badly this will scale as their projects grow larger. Sparse data representations in dynamically allocated memory are more plausible, but come with a performance hit, and would be restricted to Glulx.

It wouldn’t be impossible to implement in a performant way, but probably a lot more work than just extending the current two-value relations system.

You can do that with the current system, just takes a little extra boilerplate:

To decide whether all is well: decide no.
To decide whether things look grim: if all is well, decide no; decide yes.

Oh, granted. The negated relation actually suggested itself while I was trying to phrase the syntax for the relation-like rulebook. In retrospect, maybe I should have left it out for clarity.

Well, that and discussion on utility, feasibility and suchlike. For instance, the entire ‘number of turns‘ syntax is broken enough that I don’t know how useful such a feature would be in practice.

It’s gonna sound silly, but the main thing I want to do with Preform is create an algebraic notation for Inform. With lots of mathematical symbols.

# Definition: a thing is foo if it is in the Kitchen.

foo T := T in Kitchen

# Bar relates a thing (called X) to a thing (called Y) when 
# the weight of X is less than the weight of Y.

X bar Y := (weight X) < (weight Y)

It’s not that I think anybody wants to use this notation (although there’s always one). Rather, it will provide a useful way to talk about the capabilities of Inform without getting bogged down in English grammar.

For example, Inform can compactly say “now all gold coins are in the purse.” In propositional notation (which might or might not be the best choice for this, but let’s try it) you might write:

# "Now all gold coins are in the purse"
∀ C : coin C ⋀ gold C ⇒ C in purse

If you know this notation, you might immediately wonder if Inform can handle some variations:

# "Now all things which are either gold *or* coin are in the purse"
∀ C : coin C ⋁ gold C ⇒ C in purse

# "Now *there exists* a gold coin in the purse"
∃ C : coin C ⋀ gold C ⇒ C in purse

It turns out that Inform can do both of these, but you have to phrase them awkwardly. For the first example, you need a helper adjective (“Definition: a thing is gold-or-coin if…”)

For the second, you have to write it as “now a random gold coin is in the purse”, because the compiler doesn’t want to go picking random objects without explicit orders to. It also acts even if the proposition is already true – that is, if there is already a gold coin there, it may try to add a second one. This is perhaps not logically appropriate. But maybe it’s semantically appropriate.

The point is that it might be enlightening to talk about what the system can do, and then separately talk about how to phrase the code that does it. An abstract notation would help with this.

2 Likes

Not silly at all. It would certainly help when it comes to establishing more orthogonal abstractions. I’ve noticed Inform 7 still isn’t quite symmetric in these cases (for instance, we would expect a rulebook to be able to return a list, but when I do that it crashes the game).

Expanding on my answer to Otisdog’s suggestion, I would like there to be some better means of action redirection and action tracking. I often want to redirect, but if you do, it seems “for the nth time” or “for the nth turn” type syntax won’t be convenient. If I have a door that will only open the third time you try to open it, I cannot make “pull doorknob/open door/pull doorknob” register as three attempts at opening it, even if my syntax tells it to redirect pulling doorknob → opening door at an early stage. If that were solved, I would certainly endorse the ability to query these values at runtime as per Otisdog’s request.

The notion I was going for was to restrict the proposed relation-like rulebook to act in the form of computed relations, to enable I7 to do the heavy lifting at compile time. If I’m right, we might replace examples like What Not To Wear with something not just terser and clearer, but also (much) more performant.

As I mentioned recently in another thread, there’s some evidence that Inform was going to get tuples at some point, which would be perfect for using as rule parameters and return values.

Though speaking of asymmetries, I also noticed that verbs linked to relations can include prepositions but verbs “for saying only” can’t, which is a bit weird. Though I found a workaround for that.

Actually, you can do that:

Slippery Door
Introductory Area is a room. 

A slippery door is a door.  It is east of Introductory Area and west of Victory Monument.

A doorknob is part of the slippery door.
Instead of pulling doorknob, try opening the slippery door.

Instead of opening the slippery door for the first time, say "Your hand slips off the doorknob.  It seems some prankster has coated it in grease."
Instead of opening the slippery door for the second time, say "Your hand slips off once again.  But most of the grease has been wiped away now."
After opening the slippery door for the third time, say "You finally get a good grip on the door and swing it open."

Test me with "pull doorknob / open door / pull doorknob / e".

Where otistdog’s suggestion would be really helpful is in the description of the doorknob, so that it can have different appearance depending on how much grease is left. Currently there isn’t any good way to find that number without having something separate tracking it – while the number that I7 itself is tracking is accessible to a bit of I6 extension code, there isn’t any way to identify that you’re looking at the right action without a bit of metadata the compiler doesn’t provide outside of itself, sadly.

Fun fact: the I7 compiler currently doesn’t “unique” the conditions, so it actually has three separate counters all tracking the same condition in the above example that are all incremented at the same time, wasting both memory and processing time. So if that matters to you (which is less critical under Glulx) then you might want to do your own tracking anyway, until that gets fixed.

1 Like

Thank you! I’d forgotten the exact problem, which is egg on my face. The problem was not “for the nth time” syntax but “for the nth turn”. Consider the following mutilated version of your example:

Introductory Area is a room. 

A slippery door is a door.  It is east of Introductory Area and west of Victory Monument.

A doorknob is part of the slippery door.
Instead of pulling doorknob, try opening the slippery door.

Instead of opening the slippery door for one turn, say "Your hand slips off the doorknob.  It seems some prankster has coated it in grease."
Instead of opening the slippery door for two turns, say "Your hand slips off once again.  But most of the grease has been wiped away now."
After opening the slippery door, say "You finally get a good grip on the door and swing it open."

Test me with "pull doorknob / open door / pull doorknob / e".

This yields the following transcript:

>[1] pull doorknob Your hand slips off the doorknob. It seems some prankster has coated it in grease.

[2] open door
Your hand slips off the doorknob. It seems some prankster has coated it in grease.

[3] pull doorknob
Your hand slips off the doorknob. It seems some prankster has coated it in grease.

[4] e
(first opening the slippery door)
You finally get a good grip on the door and swing it open.

I guess it could be valuable to be able to count a repeated action as applying only when that exact action is used, but if that is true, that doesn’t really tell us why it works differently for “nth time” syntax.

for N turns is different because it’s specifically intended for consecutive turns. TBH, I wouldn’t use that for this sort of puzzle, it’s too mean to the player. It doesn’t really make much sense to use that for individual actions at all; it’s more appropriate for general conditions, such as how long a torch has been lit (to make it go out) or how long the player has been wearing wet garments (to make them dry out, or the player to catch cold), etc.

2 Likes

@Juhana, I do recall that, but the quote that you cited reads (to me) like the technical issue of space requirements has been identified as the major barrier in the current implementation, and also that a way to overcome this barrier has been outlined in theory (by switching to a sparse representation that requires less storage but comes with a performance tradeoff). By fortuitous coincidence, the infrastructure for a sparse representation is already in place: the implementation of tables! (I’ve tried to build an extension for SQL-like functionality against tables a couple of times, but the efforts have been stymied – the current obstacle being that table columns [as a kind] are not very well-documented and declaring a list of them isn’t allowed if the associated kinds of value for the table columns differ.) I cast no aspersions on the decision not to implement this so far, as I’m sure it would be a pain. But this thread is about wishes! Wishes are easy.

@mirality, yes, I am aware that that’s what must be done now, i.e. you have to write two declarations, with the second negating the first. The same used to be the case for definitions, but now there’s the rather than syntax (introduced 5U92, with a bug fix for increased reliability in 6E59). I’m only wishing that it were easier to express the two versions (positive and negative) of decide whether phrases more compactly, which I would count as a small improvement in the same category. (I did say that I hoped someone would tell me if the things I listed were easy to implement now, so I do appreciate the gesture, and it’s certainly not any more difficult to make two declarations for decide whether phrases than it was for definitions.)

@Eleas, I want to hear the wish lists of others (like your own and zarf’s intriguing posts), not bog down this thread with back-and-forth about my own, but you did say that discussions of feasibility and utility were in bounds. (I’ll be quiet now, though.) Also, if you don’t mind to share, what information is available about Preform? Is there a draft document or summary somewhere?

(Nobody has posted on this thread for a while, but it seemed worthwhile to keep it going, if only because there is no other venue for communicating feature requests at the moment.)

Another thing that I’d really like to see in the next version that is perhaps less of a reach technically: the ability to use a phrase in the same way that an adjective can be used when creating a condition. (Again, I hope that I’m just not missing something here – please correct me if so!)

I have run into this numerous times when trying to use the “is listed in” phrase for lists, or when trying to make use of my own phrases. An example of frequently-attempted incorrect code would be:

...
let target doors be the list of open doors that can be seen by the actor;
let chosen door be a random door that is listed in target doors; [won't compile]
...

Now, I know that, in that particular example, it would be easy enough to say:

...
let chosen door be a random open door that can be seen by the actor;
...

or alternatively to create a special purpose global variable and associated definition:

...
Target doors is a list of doors that varies.

Definition: A door is short-listed if it is listed in target doors.
...
let chosen door be a random open short-listed door;
...

The drawback of the first option is that if I have to make the same check in multiple contexts within the same phrase, I can’t save any work for the computer by creating the list once and reusing it – instead the same conditions must be included in any other “a random…” phrase, and the set of doors must be computed each time.

The drawback of the second option is sense of awkwardness and the need to create (and remember) assorted special-purpose globals and their associated adjectives.

By my best guess, the underlying issue is that the phrase “is listed in” requires two parameters while the adjective requires only one. However, the second parameter (target list) is constant in this usage, so there’s only one “degree of freedom”. To my untrained eyes, it seems as though it should be possible for the compiler to recognize this phrasing and to create a structure similar to that which is created manually in the second option.

I would also really like to be able to use multiple relations in a condition without creating and invoking special-purpose adjectives, as in:

...
The interest relation relates various people to various things.
The verb to be of interest to means the reversed interest relation.
...
let chosen door be a random door that can be seen by the player and that is of interest to the player; [won't compile]
...

The phrase “and that” would have to signify another sub-condition applicable to the same target as “open” and “that can be seen by the player”. So the following would be equivalent:

a random open door that can be seen by the player;
a random door that is open and that can be seen by the player;

It would also be nice to be able to use more than one form of the verb when describing a condition using a relation, as in:

...
let chosen door be a random door that the player can see; [won't compile; compare "that can be seen by the player"]
...
1 Like
let target doors be the list of open doors that can be seen by the actor;
if the number of entries in target doors is greater than zero:
    let door number be a random number from 1 to the number of entries in target doors;
    let chosen door be entry door number of target doors;
    ...

@mirality, yes, that is a third option for handling the situation in a way that will compile (alongside the other two that I listed), but the point of the post was that it would be nice for the submitted source text to compile as-is. (And that the technical reason for the inability to express it that way – assuming that one exists – may be addressable.)

Some more ideas for things that I would like to see in the next release:

First, apply the “through” part of the “number of moves from ... to ... through ...” pattern to routes through relations in general.

Right now “the number of steps via ...” or “the next step via ...” don’t allow application of conditions to the items related via the relation in question, so it’s not possible to say something like:

let gossipee be the next step from gossiper to gossip target via the grapevine relation through chatty people;

such that route-finding only happens via people that qualify as “chatty”. It would be nice if the logic that was worked out for the map-specific routines were grafted onto the general routines.

Second, move backdrops’ “range” to a relation-based mapping.

Right now the architecture is still rooted in the I6 .found_in property, which has its merits in terms of memory savings but also has drawbacks in terms of world-model accuracy. For example, a backdrop object is only in one part of the world (or off-stage) at any time according to the internal model. If you want to determine whether a backdrop “should” be present in a room that’s not the location, it isn’t easy to do so. Also, changing the background’s range must be done by redefining the range completely – it’s not possible to add or remove rooms or sets of rooms to the existing range.

Moving the basis of presence to a relation between rooms and backdrops would eliminate both of those issues. Updates to library routines shouldn’t be too extensive for the basics of moving backdrops around in accordance with existing logic. They would be more involved for purposes of improving world-model consistency, such as ensuring that a lit backdrop provides light to any room in which it is present (even when that room is not the location), that people in such rooms can see the backdrop, etc.

3 Likes

Another item for the list of wishes: something that I ran into for the second or third time again today.

It would be really nice if comparatives/superlatives could be based on something dynamically calculated instead only a value-holding property. Right now the workaround is to recalculate and update value-based properties prior to using comparatives based on them.

2 Likes