To say a plural

The inform compiler has a function to find the plural form of a word, which it uses to interpret phrases such as ‘There are 4 oxen in the meadow’ and to automatically generate default ‘printed plural name’ properties for objects from their kinds (e.g. the default ‘printed plural name’ for an ox is ‘oxen’, once an ox is defined as a kind of thing.)

This function is referred to obliquely in the Documentation 4.4:

Inform constructs plurals by a form of Conway’s pluralisation algorithm, which is quite good - for example, it gets oxen, geese (but mongooses), sheep, wildebeest, bream, vertebrae, quartos, wharves, phenomena, jackanapes and smallpox correct.

Is there any way to access this programmatically? I want to write a simple phrase starting

To say the plural of (T - a text):

No. That code doesn’t exist in the Inform library; it’s only in the compiler.

1 Like

Darn, as I feared. Have spent 4 hours looking for it :grimacing:

Don’t suppose you know if there is a published extension that provides this?

It’s frustrating that an application with natural language credentials has a ‘hidden’ implementation of something so fundamental that is nevertheless not exposed to writers. :grimacing:

There doesn’t appear to be – but that’s hardly surprising; as zarf said it’d have to be a reimplementation as the standard implementation isn’t available at runtime.

As far as I can tell, Inform will only generate the plural for kinds of thing (as single objects presumably are assumed to either not be plural or to be explicitly named with their plural name, requiring no generation). As such, the only way I’m aware of to “access” this generated name is a bit heavy-handed:

An ox is a kind of thing.  There is an ox.

To say plural of (name of kind of value K):
	carry out the printing the plural name activity with a random K.

When play begins:
	say "plural is [plural of ox]".

(In theory it should be possible to print the plural of the kind directly without creating an actual object of that kind, but that would probably require a dip into I6 code; I don’t think there’s an I7 syntax for accessing the properties of a kind directly.)

2 Likes

The closest I’ve been able to get is with the extension Object Kinds by Brady Garvin, which I have hacked slightly to make it work with 6M62.

This uses I6 hackery to temporarily instantiate the player object as an object of the specified kind, then read off the plural property of that kind, before reinstating the player with her usual kinds (or in I6-speak, class inheritances). So, you still have to create (in I7) a kind named as the word you want to find the plural for, but not an instance of that kind as an object.

Version 2 of Object Kinds by Brady Garvin begins here.

"The ability to treat object kinds as values."

[Based on RTP.i6t and The Inform Technical Manual.]

Chapter "Object Kinds as Values"

An object kind is a kind of value.
Object Kind #9999 specifies an object kind.  [I opt for an arithmetic kind of value to prevent authors from giving kinds properties; at the moment, that isn't supported.]

To decide what object kind is the generic object kind: (- 0 -).

Chapter "Yourself as the Spare Object"

Include (-
	[ ok_imbue_selfobj kind_index;
		(selfobj.&2)-->0 = KindHierarchy-->(2 * kind_index);
		(selfobj.&2)-->1 = KindHierarchy-->(2 * kind_index);
		(selfobj.&2)-->2 = KindHierarchy-->(2 * kind_index);
	];
	[ ok_unimbue_selfobj;
		(selfobj.&2)-->0 = K8_person;
		(selfobj.&2)-->1 = K2_thing;
		(selfobj.&2)-->2 = K0_kind;
	];
-)

To make yourself temporarily behave as if it has the kind (K - an object kind): (- ok_imbue_selfobj({K}); -).
To make yourself behave as if it has its proper kinds again: (- ok_unimbue_selfobj(); -).

Chapter "Counting Object Kinds on the Z-Machine" (for Z-Machine only)

To decide what object kind is the last object kind: (- (#highest_class_number - 5) -).

Chapter "Counting Object Kinds in Glulx" (for Glulx only)

Include (-
	Global get_last_object_kind = compute_and_get_last_object_kind;
-) after "Definitions.i6t".

Include (-
	Global last_object_kind;
	[ compute_and_get_last_object_kind i result;
		result = 0;
		for (i = K0_kind: i: i = i-->6) ++result;
		get_last_object_kind = just_get_last_object_kind;
		return last_object_kind = result;
	];
	[ just_get_last_object_kind;
		return last_object_kind;
	];
-).

To decide what object kind is the last object kind: (- get_last_object_kind() -).

Chapter "Operations on Object Kinds"

Include (-
	[ object_kind_is_valid kind;
		return kind >= 0 && kind <= (+ the last object kind +);
	];
-).

Definition: an object kind is valid rather than invalid if I6 routine "object_kind_is_valid" says so (it is at least zero, the minimum object kind, and at most the maximum object kind).

To repeat with (I - a nonexisting object kind variable) running through object kinds begin -- end: (- for({I} = 0: {I} < (+ the last object kind +): ++{I}) -).

To decide what object kind is the parent of (K - an object kind): (- KindHierarchy-->({K} * 2 + 1) -).

To decide what list of object kinds is the children of (K - an object kind):
	let the result be a list of object kinds;
	repeat with the candidate running through object kinds:
		if the candidate is not the generic object kind and the parent of the candidate is K:
			add the candidate to the result;
	decide on the result.

To say the singular of (K - an object kind): (-
	switch ({K}) {
		0: print "object";
		default: print (I7_Kind_Name)(KindHierarchy-->(2 * {K}));
	}
-).

OK__wrk__txt is a text that varies.
To get the plural of (K - an object kind):
	(-
	switch ({K}) {
		0: print "objects";
		1: print "rooms";
		2: print "things";
		default:
			ok_imbue_selfobj({K});
			(+ OK__wrk__txt +)=(+ yourself +).(KindHierarchy-->(2 * {K}))::plural;
			ok_unimbue_selfobj();
	}
	-).

To say the plural of (K - an object kind):
	get the plural of K;
	say "[OK__wrk__txt]".


Chapter "Identifying Object Kinds from Descriptions"

Cached object kind identification relates various descriptions of objects to one object kind.

To decide what object kind is the object kind for (D - a description of values of kind K):
	if D relates to an object kind by the cached object kind identification relation:
		decide on the object kind that D relates to by the cached object kind identification relation;
	repeat with the candidate running through object kinds:
		make yourself temporarily behave as if it has the kind the candidate;
		if yourself matches D:
			make yourself behave as if it has its proper kinds again;
			now the cached object kind identification relation relates D to the candidate;
			decide on the candidate;
	make yourself behave as if it has its proper kinds again;
	now the cached object kind identification relation relates D to the generic object kind;
	decide on the generic object kind.

Chapter "Relating Objects and Object Kinds"

Section "Private I6 Hook" - unindexed

To decide whether (O - an object) is of kind (K - an object kind) according to I6: (- ({O} ofclass (KindHierarchy-->(2 * {K}))) -).

Section "Decider, Relation, and Verb"

To decide what object kind is the object kind of (O - an object): (- ({O}.KD_Count) -).

Being of the kind relates an object (called O) to an object kind (called K) when O is of kind K according to I6.
The verb to be of kind implies the being of the kind relation.
The verb to be of the kind implies the being of the kind relation.

Object Kinds ends here.

---- DOCUMENTATION ----

### Modified by Peter Bates to make compatible with 6M62 ###

When Object Kinds is included, we can treat the kinds of objects as values in
their own right.  Ordinarily we write these values using the syntax

	the object kind for (K - a kind name in the plural)

For instance,

	showme the object kind for things;

will print

	Object Kind #2

because "thing" is the third kind defined by Inform ("object" is Object Kind #0,
and room is Object Kind #1).  We can also write kinds by number, though this is
less usually useful:

	showme Object Kind #2;

will give the same output.

In the latter case, it may be useful to test

	if (K - an object kind) is valid:
		....

or

	if (K - an object kind) is invalid:
		....

because the extension's other phrases are liable to produce error messages if we
give them an object kind that doesn't actually exist.

Other ways to obtain object kinds are via the loop

	repeat with (I - a nonexisting object kind variable) running through object kinds:
		....

or by asking an object for its kind:

	the object kind of (O - an object)

Once we have a kind, we can get its parent, which is its direct superkind, or
"object" if none exists:

	the parent of (K - an object kind)

as well as a list of its children (its direct subkinds):

	the children of (K - an object kind)

It is also possible to test whether an object has a certain kind via the being
of the kind relation.  For instance:

	if (O - an object) is of kind (K - an object kind):
		....

And we can say the preferred singular and plural forms:

	say "[the singular of (K - an object kind)]"

	say "[the plural of (K - an object kind)]"

Example: * Object Kinds Demonstration - Printing a summary of the story's kind hierarchy.

	*: "Object Kinds Demonstration"

	Include Object Kinds by Brady Garvin.

	There is a room.
	A pteranodon is a kind of animal; a military-grade flying hamster ball is a kind of device.
	Fido is a pteranodon.
	When play begins:
		say "Object kind of the player: [the singular of the object kind of the player].";
		say "Object kind of pteranodons: [the singular of the object kind for pteranodons].";
		repeat with I running through object kinds:
			say "[the singular of I] / [the plural of I]: kind of [the singular of the parent of I].[line break]    Children kinds: ";
			let child printed be false;
			repeat with J running through the children of I:
				say "[if child printed is true], [end if][the singular of J]";
				now child printed is true;
			if child printed is false:
				say "(none)";
			say ".[line break]    Whether the player is of this kind: [whether or not the player is of kind I].[line break]";
			say "    Instance things: [the list of things that are of the kind I]."
"_Test_plurals" by PB

Include Object Kinds by Brady Garvin.

The Pasture is a room.
A word is a kind of thing. A vegetable is a kind of thing.
An ox is a kind of animal. A sheep is a kind of animal. A potato is a kind of vegetable.  A cabbage is a kind of vegetable. A tractor is a kind of device.  A cloud is a kind of thing. An octopus is a kind of animal.
In the pasture is a sheep. In the pasture is an ox. In the pasture is a potato. In the pasture is a tractor.

Instead of doing something with something:
	say "Usually, [the plural of the object kind of the noun] are best left alone.".

Plural-finding is an action applying to one topic.
Understand "What is the plural of [text]" as plural-finding.

Carry out plural-finding:
	Let t-plural be "";
	Let plural-found be false;
	Let t be the substituted form of "[the topic understood]";
	repeat with OK running through object kinds:
		if the substituted form of "[the singular of OK]" is t:
			now plural-found is true;
			break;
	if plural-found is true:
		let t-plural be the substituted form of "[the plural of OK]";
	else:
		let t-plural be "unknown";
	say "The plural of [t] is [t-plural].".
	
Test me with "what is the plural of ox / what is the plural of word / what is the plural of sheep / what is the plural of vegetable / what is the plural of potato / what is the plural of tractor / what is the plural of octopus  / what is the plural of dwarf / take sheep and ox / take all".

That’s an interesting trick but I wouldn’t recommend using the player (or any other existing object) as your stand-in, as this might have some property values explicitly overridden – thus would report these rather than the value actually set on the kind.

But it should be safe to instead create one generic thing to use as a stand-in and mess with this object’s kind rather than the player’s. (Under the assumption that this is less likely to have custom values set.) That’s especially easy if you create the object itself in I6 code, because then it’s not even possible to refer to it from I7 (without extra effort).

1 Like

Yes, the same thought had occurred to me. I’m not up enough on the workings of the compiler to know whether the class inheritances on the player object that are being hacked here are constant and (usually) immutable as the code appears to assume. I guess they could be saved and restored rather than assumed as they are here. I assume the extension chose the player object to hack because it’s one of the few objects guaranteed to be constantly present in every story.