Five Days with Inform 7: a retrospective review

As a side note, this is another place where I think Dialog has a useful innovation that I7 could adopt. With Dialog, there are effectively two ways to invoke a rulebook: you can “query” it, which means “run each rule in turn until one of them succeeds”, or you can “exhaust” it, which means “run every single rule, and keep going whether they succeed or fail”. The standard library uses queries for Check, Carry Out, and Report, but exhausts for Before, After, and Every Turn.

I never put insteads at the end of a line. Any line that’s going to ‘instead out’, I make it start with the instead. This makes them instantly identifiable.

-Wade

4 Likes

Are there cases where it would make sense to both query and exhaust the same rulebook?

Adding an exhaust option to I7 wouldn’t be too tricky, but an even better option could be to be able to set specific rulebooks as non-interruptable, and then try to detect and raise a warning/error if you use one of the rtrue phrases within them.

(My pet peeve is that I6 functions return 1 by default rather than 0. So when you’re writing I6 rules you have to ensure you have an rfalse or else you can accidentally stop a rulebook.)

1 Like

In Dialog, occasionally, but I think you’re right that in I7 having it be a setting on the rulebook would be better.

The risk is that it becomes another source of confusion, like how some action-processing rulebooks have default success and others have default make no decision, but that would also be a risk with the “exhaust” phrasing, and making it be an error to “stop” them would mostly solve that.

Just coming here to say that I wrote a statement of the form “if x is more than 3” for the 316th time, and for the 316th time I learned that it should be “if x is greater than 3”. That’s my twenty years with Inform 7 for you. :smiley:

7 Likes

Don’t just live with it, fix it!

To decide whether (n - a number) is more than (m - a number):
	decide on whether or not n is greater than m.

This would be worth putting in Basic Inform, honestly:

To decide whether (n - a number) is more/greater than (m - a number):
	(- {n} > {m} -).

Don’t non-strict checks ( ≥ and ≤ ) already have two phrasings? Or do they force you to write “at most” and “at least” instead of “greater than or equal to” and “less than or equal to”?

(Speaking of which, the characters ≤ and ≥ should totally work too. Haven’t checked if they do.)

5 Likes

That’s actually interesting: should we be submitting bug reports/improvement requests for synonyms for common functions? Should I ask to add ‘when scene_x starts’ to be added as a synonym for ‘when scene_x begins’[1]? ‘Final Check…’ as well as ‘Last Check…’?

For that matter, can I name my scenes with synonyms? So I can refer to a single scene as both ‘Everything Falls’ and also ‘Everything Is Falling’?


  1. Edit! I misremembered this problem as ‘if x has started’, which doesn’t actually exist. Thanks Zed! ↩︎

3 Likes

I would, personally, if the words are truly synonymous in English (like “at least”, “greater than or equal to”, and “≥”). Given the glacial pace of development, though, it’s probably easier just to define them yourself!

You can, though this one’s a bit more awkward.

To decide what scene is Everything Falls: decide on Everything Is Falling.

Inform uses the same syntax for calling a phrase and dereferencing a value, so you can use phrases without arguments as proxies for values.

4 Likes

You can also make a scene constant:

There is a scene called everything is falling. "all fall down"

Everything falls is always everything is falling.

when play begins: say description of everything falls.

…but I wouldn’t recommend it. Several of the fundamentals about scenes involve compiler special-cases where only the scene name as asserted in its creation will do and it’d be tedious and error-prone to keep track of which those are. Any old scene value is fine in phrases (like if <scene> is happening) and I’m frankly shocked it also works with when <scene> begins: and when <scene> ends:.

But it doesn’t work in <scene> begins when [...] or <scene> ends when [...] assertions or in during clauses in rule preambles.

I have sometimes used something similar to cut down some of the verbosity around relations. If you have:

Labeling relates various texts to one person.

The verb to tag means the labeling relation.

"protagonist" tags the player.

when play begins:
  repeat with x running through the texts that tag the player begin;
    say x;
  end repeat;

it’ll fail to compile with

In ‘repeat with x running through the texts that tag the player’, you seem to want to repeat through all possible values which have the kind ‘a text’, and there are just too many of those. For instance, you can ‘repeat with D running through doors’ because there are only a small number of doors, but you can’t ‘repeat with N running through numbers’ because numbers are without end.

Verbs won’t let you do some things you want to do with relations; for some things, you need the phrases that require a relation-value. So one can slightly cut down the verbosity with:

Tagging is a relation of texts to person variable.
Tagging is initially the labeling relation.

when play begins:
  repeat with x running through the list of texts that relate to the player by tagging begin;
    say x;
  end repeat;

…which’ll end up with an out-of-range memory access runtime error in 10.1. But it works in current development inform.

Unfortunately, one can’t just use always to create a relation-valued constant here; the compiler demands more explicit typing for dislike.


Then again, a while back, I posted code to loop through relations without building a list for current dev Inform; if I recall correctly, that code as given has ceased working in subsequent revisions of current dev. But usefully, Otis back-ported such relation loops for 9.3/6M62 and then I forward-ported the back-port for relation loops in 10.1. So ultimately I’d probably use this rather than the relation-value variable.

4 Likes

greater than is implemented as a verb/relation, so we’d might as well follow in its tracks:

The verb to be more than means the numerically-greater-than relation.

In 10.1, the compiler isn’t happy with or , but in current dev, adding this works just fine:

The operator ≤ means the numerically-less-than-or-equal-to relation.
The operator ≥ means the numerically-greater-than-or-equal-to relation.
5 Likes

I’m sure some could be useful, but in my opinion restraint is in order, too. Accurately documenting such synonyms would make things just a little bit longer and more complicated, increasing the cognitive load in apprehending a given thing… maybe by only a tiny bit for a given item, but it could add up.

And Inform’s compiler already has a very hard problem. Adding a bunch of synonyms would make that job harder and create more opportunities for it to get confused by ambiguities which, in turn, would make it more likely that the errors it reports end up being completely baffling to authors.

So I’m concerned that multiplying synonyms runs a risk of doing net harm to the goal of ease of use.

Again, a given specific one might be a great idea! But I think the compiler shouldn’t aspire to match the standards a diligent parser-game author might have for their game in being permissive and trying to anticipate all the ways a user might try to say something.

1 Like

That’s a good point! As another option, how hard would it be to give better error messages for common synonyms, instead?

OK, I finally realized that ‘first check’ and ‘last check’ are inappropriate for my ‘open door’ checks.

The problem is that the door is closed, guarded, and locked, and needs to be checked in that order:

  • if the door is already open, say ‘it’s already open’ (library message)
  • Otherwise if the door is guarded say “it’s guarded” (my message)
  • Otherwise if the door is locked say “It’s locked” (library message)

So my ‘it’s guarded’ message can’t be ‘first check’, because then you’re told you can’t open the door because it’s guarded, but it’s actually already open. And it can’t be ‘last check’ because then you’re told the door is locked, but you actually can’t get close enough to tell, because it’s guarded.

Is this the sort of thing the ‘list the X rule after the Y rule’ constructs were created for? Can I do this?

1 Like

I wouldn’t go to rule reordering for this. Slightly off-topic – once I learned in life that you can turn rules off in circumstances (e.g. ‘The stop the player opening a guarded door rule does nothing if the door is open’), I found I didn’t have to do much reordering any more. EDIT - But I won’t use that trick here, either.

In your current case, if the two library messages are going to hit in the correct order (a complaint that the door is open first, or that it’s locked after that) I’d put a clause on rule 2 (the guarded checking rule) like so:

Check opening the door when the door is guarded:
	if the door is open:
		continue the action;[will pass on through to library message checks, the first of which is 'The door is already open']
	otherwise:
		instead say "I can't open the door while it's guarded.";

Now this check rule will run first out of the three, as it’s the most specific, and if it sees the door is guarded, it traffic lights the action to a failure right now if the door is also closed, or lets it through to a default ‘it’s already open’ message if it is open.

If this was me, I would probably write one check rule that handles all three cases with specific messages, dispensing with the library. But that’s me.

-Wade

2 Likes

Or even

Check opening the closed guarded door:
2 Likes

Yes, but Zarf’s suggestion above is also a neat and economical one.

Sadly, that doesn’t work in 10.1 due to a bug.

OK, this is all super helpful, and I keep getting a better sense for everything.

Part of my goal here is to understand how I7 works, but another goal is to figure out how to use it effectively, and I think Wade’s comment at the end of his post is salient for this:

At this point, I’m inclined to agree!

The process for me seems to be:

  • The fastest thing to do is to just use ‘check’ rules and not worry about it.
  • If this turns out to be insufficient, maybe try ‘first’ or ‘last’.
  • Once this also turns out to be insufficient, maybe just roll your own full solution!

At that point you’re spending a lot of effort thinking about the particular situation. And here, it might be advantageous to just write new text for the player. Because new text is what your game is all about, and spending a ton of effort to just get the correct library messages to the player isn’t improving the player experience as much as writing your own new messages would be, especially if you can also inject characterization or backstory or anything else into those messages–something the library obviously could never do.

I imagine in some complicated systems, it’d be worth it to get the system messages queued up instead. Perhaps the text is going to show up in enough different situations that going no-characterization/no-backstory is the only thing that works. Perhaps it’s concerning an area of the game that you don’t want the player messing with: there’s nothing that tells a player ‘mess with this more’ than unique text!

But for me in this particular situation, there’s a grand total of two guarded doors, they’re both guarded differently, and I absolutely want the player to mess with them a lot to get a better sense of what’s going on. So handling everything myself it is!

4 Likes

WHAT?! Well, I guess it’ll get fixed later. But my jaw almost fell off at this.

-Wade

It’s fixed in the current development build (and actually has been for several years), but unfortunately a new official release is unlikely to come any time soon.

Pretty sure I remember using this successfully in 10.1… or maybe it was something else similar but not quite the same…