Referencing a particular instance of a generic object

I’m new to Inform (very experienced in python and various other languages). I’ve been scouring the forums for the past three hours. What I’m trying to do is essentially reference a generic object (that is, a generic instance of a kind). Here’s a minimal example:

A bed is a kind of supporter. A bed is always fixed in place.
A marble is a kind of thing.

My Room is a room.
In My Room is a bed. It supports a marble.
Your Room is east of My Room.
In Your Room is a bed. It supports a marble.

There is a gold key.
After jumping in Your Room:
	move the gold key to [entry 1 of the list of beds which are in Your Room].

What’s curious about this is how “It” refers to the generic object when I say “It supports a marble.” There’s some sort of temporary reference here. However, that same “It” seems to be gone for good afterwards. Is there a correct, canonical way to specify a particular instance of a generic object within a phrase at runtime, so that you can do something with it?

The other approach would be to give these generic objects identifiers. Something like this:

A bed is a kind of supporter. A bed is always fixed in place. The printed name of a bed is usually "bed".

My Room is a room.
A bed_01 is a bed in My Room. It has description "This is my bed."
Your Room is east of My Room.
A bed_02 is a bed in Your Room. It has description "This is your bed."

Understand the printed name property as describing a thing.

However, this approach means keeping track of dummy variables and makes the code a lot less elegant. It also introduces confusion about grouping items (see this for a related discussion). Another hint that this is wrong is that the phrase “Understand the printed name property as describing a thing.” doesn’t show up anywhere in the documentation. So… this doesn’t seem like the Right Way to do it, either.

Ultimately, it looks like the right approach would be to declare generic objects and then have some way to refer to them later, as I had earlier: “move the gold key to [entry 1 of the list of beds which are in Your Room]”

Although even that’s ugly because of the lack of assertion. Something even more ghastly would be:

thething is an object that varies.
After jumping in Your Room:
	let L be a list of objects;
	now L is [the list of beds in Your Room];
	assert [the number of entries in L] is 1;
	now thething is [entry 1 of L];
	move the key to thething;

It’s clear that I’m completely on the wrong track. Anyone able to point me in the right direction?

The usual way of making such a reference is “a random bed”.

There is a gold key.
Carry out jumping:
     move the gold key to a random bed in Your Room.
My Room is a room.
A bed_01 is a bed in My Room. It has description "This is my bed."
Your Room is east of My Room.
A bed_02 is a bed in Your Room. It has description "This is your bed."

This isn’t wrong, it just depends on whether you want the objects to be truly indistinguishable to the player. Since (in this example) the beds are fixed in different rooms, grouping and distinguishability aren’t an issue. And it’s not “dummy variables” – it’s giving objects explicit source-code names. It’s not bad style (again, if you don’t care about grouping) (and you can add grouping behavior back in later if you want).

You can still use the “a random foo” trick to select the appropriate bed in a given context.

If you are testing a condition, you can often phrase it more neatly with a “called” label:

After jumping:
	if a bed (called B) is in the location:
		say "You jump on [the B].";
	else:
		say "No beds here.";

These are great tips – thanks everyone!

Alright, I’ve run into one more issue with this. It doesn’t matter if I use the anonymous naming (“in My Room is a bed.”) or explicit identifiers (“bed_01 is a bed in My Room”). I’m unable to refer to an object’s history:

A thing can be dirty or spotless. A thing is usually spotless.

Understand the command "clean" as something new.
Understand "clean [something]" as cleaning. Cleaning is an action applying to one touchable thing and requiring light.
Carry out cleaning something: now the noun is spotless.

A bed is a kind of fixed in place supporter.

My Room is a room.
bed_01 is a bed in My Room. It is dirty.
bed_02 is a bed in My Room. It is dirty.

After looking:
	if a bed (called X) is in the location:
		Let Y be a random bed in the location;
		if Y was dirty:
			say "[Y] was dirty at the end of the last turn, and ";
		else:
			say "[Y] was spotless at the end of the last turn, and ";
		if Y is dirty:
			say "it is dirty right now.";
		else:
			say "it is spotless right now.";

In my particular use case I’m trying to report the before-and-after condition of something, from a pool of various anonymous things (i.e., in the vein of “In the Lab are 15 triangles and 10 squares.”).

This particular issue is covered in the documentation, Section 9.13:

First, I don’t understand the fundamental issue in the first place. In my example above, Inform makes Y a reference to one of the beds. I don’t care about the history of Y (the pointer to the bed), I care about the history of *Y (the bed it’s pointing at). What I would expect is for Inform to dereference Y to bed_01 or bed_02 as was randomly chosen, and then use the history of that object to answer the question about whether it was dirty or spotless at the end of the previous turn.

So, there’s still something I’m not understanding. Presuming I don’t know how many beds (or triangles, or squares) are in a room, is there a nice way to check the history of one of them?

I don’t know if there’s anything to say about this other than “That’s not what Inform does when you use the past tense in your source code.” Whatever Inform does to keep track of past-tense things, it doesn’t dereference the variable. Here’s the explanation of what’s going on from Appendix B, which is the annotated Inform 6 template (this is from an older version of Inform but it probably still holds good:

Which means–I think–that every time you put something like “If the largest thing was dirty” in your source code, then Inform makes a word that every turn tests “Is the largest thing dirty?” and stores that in a chronological record. Which means that it’s always testing the largest thing at that time. It has no way of knowing what the largest thing is going to be next turn, when you run the “If the largest thing was dirty” test.

In philosophy, we’d say that in “If the largest thing was dirty” “the largest thing” is being used de dicto rather than de re… that probably isn’t helpful as an explanation. What I think it means in programming terms is that it doesn’t dereference the variable, and can’t, because Inform doesn’t by default keep track of a record of whether every thing was dirty. It just keeps track of the past-tense conditions that you’ve defined.

The way to accomplish what you want is probably to define another property that keeps track of whether a thing was dirty last turn, and update it appropriately:

[code]A thing can be dirty or spotless. A thing is usually spotless.

A thing can be formerly dirty or formerly spotless. A thing is usually formerly spotless.

Last every turn (this is the cache dirtiness rule):
now every dirty thing is formerly dirty;
now every spotless thing is formerly spotless.

When play begins: follow the cache dirtiness rule. [this sets everything the first time]

Understand the command “clean” as something new.
Understand “clean [something]” as cleaning. Cleaning is an action applying to one touchable thing and requiring light.
Carry out cleaning something: now the noun is spotless.

A bed is a kind of fixed in place supporter.

My Room is a room.
bed_01 is a bed in My Room. It is dirty.
bed_02 is a bed in My Room. It is dirty.

After looking:
if a bed (called X) is in the location:
Let Y be a random bed in the location;
if Y is formerly dirty:
say "[Y] was dirty at the end of the last turn, and ";
else:
say "[Y] was spotless at the end of the last turn, and ";
if Y is dirty:
say “it is dirty right now.”;
else:
say “it is spotless right now.”;[/code]

If you need a longer record, you could give every thing a list of past cleanliness states–although this seems like it would get memory-hoggy very fast if you have a lot of things involved, so you might want to restrict cleanliness states to the particular kinds for which they’re relevant (e.g. “A bed can be spotless or dirty” rather than “A thing can be spotless or dirty”) and truncate them as needed. But anyway, if you want to track particular things that you’re going to pick out using random variables, you need to handroll your own system rather than relying on Inform’s built-in past tense.

matt w, thank you very much for the clarifications; that helps. I think you’re right about “It just keeps track of the past-tense conditions that you’ve defined” based on the description, and indeed it would be wastelessly memory intensive to statefully track the condition of all objects throughout history.

As for “A bed can be spotless or dirty” rather than “A thing can be spotless or dirty”, yes - I did that on purpose; it helps delineate the scope of what I was trying to accomplish. If keeping a list of past states is a necessity, then restricting the properties to particular kinds would help, most certainly.

I’m considering this topic resolved. Thanks again to everyone!

Though to be clear I don’t think it’s that memory-intensive to give every thing an additional on-off property; it’s giving every thing an ever-expanding list that will cause the trouble. If you don’t need the full list power you can come up with workarounds, depending on what you do need.

Also, off the main topic but it might be worth noting that Inform has much more compact ways to say text with alternatives. This:

After looking: if a bed (called X) is in the location: Let Y be a random bed in the location; say "[Y] was [if Y is formerly dirty]dirty[otherwise]spotless[end if] at the end of the last turn, and it is [if Y is dirty]dirty[otherwise]spotless[end if] right now."

accomplishes the same thing as the rule with the “if” statements on different lines.

Generally speaking, any statement that tracks the history of something must refer explicitly to a specific thing – you can’t use a variable. I.e., you can say

If bed_01 has been dirty, blah blah blah.

But you can’t say

After jumping on a bed (called B): if B has been dirty, blah blah blah.