Inform 7
for
Programmers







Ron Newcomb
6 Jan 2009
build 5U92

Contents



Inform 7: In a Nutshell


Other than being a highly domain-specific language, the deepest differences between Inform 7 and most everything else in programmer-land are:



The language Inform 7 ("I7") is:


Quick side notes:



Types, Variables, and Objects


We'll start by showing some variable declarations of every major type. For now, know that "is" and "are" are perfectly synonymous, and the articles "some", "a", "the", and "an" are simply stripped from the source.
        X is a number that varies. Y is a number variable.
        Deadline is a time that varies.
        An excuse is some text that varies.
        My favorite toy is a thing that varies.
        The current manager is a person that varies.
        The ocean currents are a direction that varies.
        The best spot is a room that varies.
        The light switch's boolean is a truth state that varies.
        The guru's answers are a table-name that varies.
        My secret plans are a rulebook that varies.
        What worked last time is a rule that varies.
        My regex target is some indexed text that varies.
        An abeyance is a stored action that varies.

Now how about some object instances, and a few relations between them. The following sample can be compiled because there is at least one room instance (assuming the very first line of the source has the work's title in double quotes; this line is automatically added).

        The apartment is a room. "Behold Bob's apartment. That smell is coming from the pile of dishes in the sink."

        The apartment complex's lobby is south of the apartment. "Rows of mailboxes are set into the wall. Box 114 is Bob's."

        Mr Bob Dobalena is a man in the apartment. "Bob is wearing an old Transformers t-shirt with some well-loved jeans."

        T-shirt, jeans, and a pair of shoes are wearable things. Bob is wearing the shoes, the T-shirt, and the jeans.

We have two Room instances ("apartment" and "apartment complex's lobby"), one Man instance, and three Thing instances ("T-shirt", "jeans", "pair of shoes") with their boolean "wearable" property set to true. The quoted text that's just floating by itself out there on its own is either put into the "description" property (for Rooms) or the "initial appearance" property (for other objects). There's two relations in there as well. Relations link two objects together, just like the standard is-a and has-a relations in OOP. Here we have the mapping-south relation (closely related to the Direction class), and the wearing relation thrice initialized between Bob and each article of clothing. (More on relations later.)

Note the following examples of conciseness: object names can be abbreviated ("Bob", "shoes"), one sentence can instantiate multiple objects (the three pieces of clothing "are ... things"), boolean properties have named values that act like adjectives (the value "wearable"; the property itself is anonymous), such a property can be set for multiple instances simultaneously (all three pieces are "wearable things"), and a relation can be set between multiple objects simultaneously (the "is wearing..." sentence).

Since some relations were initialized to true, we might as well show some other examples of supplying initial values.

        X is usually 2. Y is 5.
        Deadline is usually 4:30 pm.
        The excuse is usually "I didn't know."
        My favorite toy is usually the red Porsche.
        The light switch's boolean is usually true.
        The guru's answers are usually the Table Of Deep Answers.

The word "usually" isn't strictly needed for a variable, but a class property's initial value will be used for all of the class's instances unless the instance specifically says otherwise.



Class And Prejudice


Here's the built-in class hierarchy, a total of sixteen classes. (Also, where other languages say "class", Inform says "kind".) Inform purposely keeps its library lean.

object
        direction
        room
        region
        thing
                door
                container
                        vehicle
                        player's holdall
                supporter
                backdrop
                device
                person
                        man
                        woman
                        animal

A quick rundown: Room is a discrete location, a place; Region is a container for Rooms. The class Container means an in-game prop, such as a backpack or hamster cage. A Supporter is a chair, table, mantel, or other horizontal surface Bob can place things on top of. The player's holdall is usually a singleton, as it's a container without load limits. A instance of Door can explicitly connect Rooms, and can be open & closed if not also locked & unlocked. Direction does NOT connect rooms -- relations do that -- but is used in code to reference the same. Backdrop always has the boolean property "scenery" set, and is used for things like the sun in the sky, the faint but ever-present sound of a nearby creek, and other non-portable objects that need to remain in the parser's scope while the protagonist travels across several Rooms. Device is something that can be switched on/off. Animal is treated as a kind of person, just as pets are.

The positioning of Animal is our first hint that we're stepping out of a scientific worldview, and into a humanistic one. Likewise, the purpose of the language as a whole is to produce works of art, not software tools.

Making a subclass is straightforward:
        An archway is a kind of door.
        An archway has a number called the horizontal clearance. It is usually 6.
        An archway is always open.
        An archway can be magic or mundane. An archway is usually not magic.

This subclasses archway from door. It gives it a new numeric property call the "horizontal clearance" with an initial value that can be overridden by a particular instance. Then it permanently sets the pre-existing open/closed property to open; attempts to later code a closed archway will result in a compiler error. Finally, it gives it a new anonymous boolean property with named values "magic" and "mundane" (as opposed to a property called "magic" with values "true" and "false"). It initializes this to "mundane". We could have just said "An archway can be magic" and it would still work, but naming the antonym usually improves readability.

One instance of Person is always provided for us: "yourself". "The player" is a Person variable initialized to "yourself" unless defined otherwise. (The player refers to his avatar's Person instance as ME.) One instance of Object (the root-level class) is always provided: "nothing". (Occasionally synonyms nowhere, nobody, no-one, and no one may be used, but it's usually a special-case syntax.)



The Coding Imperative


Here are examples of all the basic imperatives. Read the print statements within.

Semicolons divide statements as usual, but the last semicolon should instead be a period. Alternately, the last statement should be followed by a blank line. This is how the end of a function is signified.

Inform calls the above imperatives, and other things like them, "phrases". Functions are also called phrases, because they are invoked similarly. But rules are different; they invoke themselves based on game events and other worldsim situations. Here's some quick examples just so we'll have somewhere to try out our imperative code.
        When play begins: say "Hello world!".
        Every turn: say "La-dee-da."
        Instead of taking yourself, say "You pull yourself up by your bootstraps and read on."

Remember to instantiate a room.



Boolean Adjectives


Adjectives, and the Description type that invokes them, are Inform's big win over the COBOL and HyperTalk families of natural-esque programming languages. Typically used for objects, a description uses combinations of adjectives with a class to declaratively create a set of instantiations. This is responsible for a great deal of Inform's conciseness over traditional programming languages. First we'll look at how to define the adjectives.

There are a few ways to define a boolean adjective. The first creates an anonymous boolean property with named values.
        A thing can be spiffy.
        A person can be grumpy or happy.

        now the iPod is not spiffy;
        if Mary is happy, change Bob to grumpy;

Inform of course understands "not spiffy", but it also understands "not grumpy" as synonymous with "happy" and vice-versa. Also, since Person is subclassed from Thing, a person can also be spiffy. Frequently in OOPLs, new properties and methods cannot be added to a pre-existing class because doing so would break pre-existing code that uses it; subclassing is required to add such embellishments. But since Inform isn't designed for re-usable code or even team-built works, Inform allows the direct modification of classes, and those changes are propagated down the subclasses regardless whether they are built-in or not. The allowed modifications are additive only, however. (But see the Hacking section at the end.)

The second way to define an adjective works like adding a boolean-returning method to a class. It also has a one-liner version using "if" instead of a second colon:
        Definition: a person is boring: [...]; [...]; decide no.
        Definition: a person is unlikable if it is boring or it is grumpy.
        Definition: a person (called the academic) is laconic rather than chatty if the academic is [...].

The word "it" is used to refer to the object on which the method is called, where traditional languages use "self" or "this" or some such. This can be customized with the "called" parenthetical. An antonym may be defined with the "rather than" phrase. The "Definition:" adjective definition isn't restricted to objects: almost any value that can be put into a variable can use the one-liner version (see the "is an X that varies" samples up top). Finally, this is one of the places where the identifier -- the adjective -- must be a single word. Hyphenated names are common here.

The third way, using a generic "to decide whether" boolean function (see below) is the least flexible because adjectives defined this way cannot combine with other adjectives. It works like a traditional boolean function. Not all things in our code will be implemented as objects: table rows, scenes, rules, rulebooks, named values, etc. Some calculated values really don't "belong" to anything or anywhere. But we frequently want these non-objects to emulate the fluid description style of objects, and the generic boolean "to decide whether" function is a primary way to do this.

The adjectives are checked the same way regardless of the storage versus calculation implementations.
        repeat with associate running through every chatty not grumpy spiffy person begin;
                say "Hi [associate].";
        end repeat;

So, changing an adjective from a variable implementation to a calculated implementation (or vice-versa) only requires adding or removing any lines that explicitly set the adjective's value; client code that merely checks the value needn't change.



Patterned Procedures


A function's "name" in Inform 7 is not an exact match of a single identifier, but a very simple textual pattern. It's easier to show this than explain, so, for functions that do not return a value:
        To plainly greet (friend - a person):
                say "Hi [friend]."

        To ponder/mull over/-- (good point - a thing) for (awhile - a time period) as (ponderer - a person):
                say "[Ponderer] sits back in [his-her] chair for about [awhile]. 'Hm, [good point] is a very good point, sir.'"

        plainly greet Dr. Muller;

(These are all equivalent:)
        ponder the best idea yet for 7 minutes as Dr. Muller;
        ponder over the best idea yet for 7 minutes as Dr. Muller;
        mull best idea yet for 7 minutes as Muller;
        mull over the best idea yet for 7 minutes as Dr. Muller;

The / forward slash divides synonyms, cannot be surrounded by spaces, and its effect ends at the first space; it's a high-precedence operator. The -- double-dash means the word(s) are optional. That is the full extent of the pattern-matching. Parameters are in parenthesis: the parameter's name precedes a hyphen and its type. Simple values are passed by-value only; objects, by-reference only. A parameter can immediately follow the "To" that begins the definition, but, two parameters cannot sit immediately side-by-side:
        To (ponderer - a person) ponders/ponder/mulls/mull (good point - a thing): [ok].
        To ponder for (awhile - a time period) (good point - a thing): [error!].

"Let" creates a local variable. "Let" is also how we create the aggregate types like "list" and "indexed text", since they cannot be statically allocated, and the language does not allow dynamic allocation.
        let X be 5;
        let the typical exclamation be "That's cool!";
        let the articles of clothing be the list of things worn by Bob;
        let M be { the red Porsche, the pair of shoes };
        let the modifiable exclamation be indexed text; [ These work... ]
        let the modifiable exclamation be "That's cool!"; [ ...as a pair. ]
        
"Stop" is the plain-jane return statement. It isn't used much, partly because of some other synonyms for return, and partly as fallout from the rules-based structure of the language.

A final natural-language feature cloaks a bitfield as a series of comma-separated sub-phrases:
        To go hiking, into the woods or up the mountain:
                if into the woods then say "Watch out for badgers.";
                if up the mountain then say "Better take your compass.";
                say "You go hiking."

Clever "naming" not only affords client code that is easy to read, but also creates library invocations that are easy to make a half-remembered guess at. The latter is, in practice, a wonderful time-saver. When we define functions, we should take client code readability into account. For example, we needn't add the articles in front of a parameter, because the parameter itself will eat it:
        To ponder the/an/a/-- (nefarious plans - a rulebook): [ unnecessary ].
        To ponder (nefarious plans - a rulebook): [ better ].

But we should explicitly add the articles if it occurs elsewhere, such as this example that pretends to understand an adjective in some cases:
        To ponder the/an/a/-- foiled/new/-- (nefarious plans - a rulebook): [...].

"To" phrases tend not to be used too much, for the similar reason that methods in a OOPL tend to reduce global function use. Instead, Inform 7 has rules, grouped into rulebooks, which we'll get to shortly. To phrases do have a nicer invocation syntax and essentially unlimited arity to recommend them over rules, but they lack the flexibility of rulebooks, as we'll see below.



Functions Decide on a Value


Because functions that return a value must have those values used in a larger statement (strong-typing disallows throwing them away), their names tend to be noun phrases rather than sentences.
        To decide which room is my favorite place: [...]; decide on My Bedroom.
        To decide what person is brother to/of (sibling - a person): [...]; decide sibling.
        To decide which object is my fabulous doodad: decide on a random thing.

        ponder the best idea in my favorite place;
        if the brother of the noun is not the noun, say "[Noun] has a brother, [Brother of the noun].";
        if my fabulous doodad is nothing, say "I'm fresh out of fabulous.";

The keywords "decide which/what" and "is" wrap around the returned type. The function's name is only that part between "is" and the colon. The return statement is now "decide on/--". Due to strong-typing and "nothing" being implemented as an instantiation of class Object, we cannot "decide on nothing" except when deciding "what/which object is".

Boolean functions must use the slightly different "whether/if" variation, and the name lies between the "whether/if" and the colon. Since they are invoked from if statements and rule headers, their names are frequently clauses sans subordinating conjunction.
        To decide whether (pants - a thing) is/are on fire:
                decide on whether or not a random chance of 1 in 2 succeeds.

        if the brother of the noun is on fire, say "That's gonna leave a mark.";

The phrase "whether or not" typecasts an if-condition ("a random chance of M in N succeeds") to a truth state ("yes/no"; a boolean), which can then be returned.



Say Phrases


It is so common to slightly vary some prose for a given situation that Inform specifically provides for procedures called from within a Say statement's prose. "To say" phrases are in a box separate from "To" procedures and "To Decide" functions, but otherwise work identically. They are invoked by square brackets.

Gendered pronouns are a common case, and most are built-in.
        To say He-She for (P - a person):
                if P is plural begin;
                        say "They";
                otherwise;
                        if P is female, say "She";
                        otherwise say "He";
                end if.

        To say (P - a person) mulls/ponders --/over (idea - a thing): [...].

        [...]; say "[He-She for Chris] glances at you[Chris ponders tar-and-feathering].";

Putting a bare object or variable name within the square brackets prints the entity's name or variable's value, respectively. This works for nearly every type in the language, though can usually be overridden like so:
        To say (code - a rule): abide by the code.
        
That would execute the rule or rulebook from within prose, rather than printing something.
        say "Chris seems to make a decision.[the formulate plans rules] But you don't know what.";

Inform has built-in a number of basic imperatives for say phrases. The docs have the full list, and the Extensions chapter has information on creating new multi-part To Say constructions.
        say "He put on [if the jeans are stained]yesterday's[otherwise]his[end if] jeans.";
        say "The weather was [one of]rainy[or]sunny[or]windy[at random].";


Sweet Relations


In lieu of numerical relationships, qualitative either/or relationships frequent interactive fiction. Relations disguise object-to-object 2D boolean arrays behind some of the best syntactic sugar in the language. Though a relation is declared with a single-word noun (a second place where the identifier must be a singular word), in initializations and invocations relations use the infix-like form of English verbs. Reciprocal (symmetric), non-reciprocal (asymmetric), and grouped (equivalence) relations are possible, as are synonymous and commutated forms for the verbs.

        Marriage relates one person to another (called the spouse). ["to another" means a symmetric relation]
        The verb to be married to implies the marriage relation.

        Bob is married to Jane.
        [...]; if Jane is married to Bob, [...]

        Friendship relates various people to various people. [an asymmetric relation]
        The verb to be friends with implies the friendship relation.
        The verb to be befriended by implies the reversed friendship relation. [ defining a "reversed" syntax for asymmetric relations is useful in Descriptions ]

        if the brother of the noun is married to the second noun, now the noun befriends the second noun;
        repeat with associate running through every person who is friends with the brother of the noun begin;
                [...];
        end repeat;

        Trust relates people to each other in groups. [an equivalence relation]
        The verb to trust (he trusts, they trust, he trusted, it is trusted, he is trusting) implies the Trust relation.

When we define a verb like "to be X", Inform can automatically conjugate it. But if we use the "to X" form, we must supply the other five forms in parenthesis. Also, "to be X" can use multiple words: "to be X with", for example. But "to X" must be a single word. Additionally, we can define the verb as "to be able to X", which means the relation is used like "can X".
        Memorability relates various people to various things.
        The verb to be able to remember (he is remembered) implies the memorability relation.

        Bob is a man. The time he spent in jail is a thing.
        Bob can remember the time he spent in jail.

Though only an asymmetric various-to-various relation actually needs a whole 2D array, the other kinds of relations are some sort of subset of one. Any relation with a singular is implemented as a property, as are equivalence relations.

The complete list of relation types are declared using one of these phrases; a "called" parenthetical (such as the spouse, above) is allowed on any singular.
        ...one person to one person...
        ...various people to one person...
        ...one person to various people...
        ...various people to various people...
        ...one person to another... [ one-to-one, symmetric (if A~B, then also B~A; think "commutative operator")]
        ...people to each other... [various-to-various, symmetric]
        ...people to each other in groups... [equivalence (think "if A equals B, then A also equals everything that B equals)]

Much like boolean adjectives, relations can be implemented as calculation rather than storage.
        Siblinghood relates a person (called X) to a person (called Y) when X is the brother of Y or X is the sister of Y.
        The verb to be a sibling of implies the siblinghood relation.

The value-checking syntax is the same, so swapping between calculation and storage is almost automatic. (Any line that explicitly sets a calculated relation will cause an error, but the code that checks the relation remains unchanged.) It is worth nothing that the relativistic relations of math are implemented this way ("The verb to be greater than implies the...") as well as almost all spatial relations (under, on, in, northeast of, etc.)

This brings us to non-object relations. Named values can take part in relations, as can numbers. But numbers, as there are an infinite variety of them, can only be part of a calculated relation, never a stored one. The types can mix and match.

But, as wonderful-looking as relations are, they are second-class citizens. We can't create a relation variable, nor return them from functions. And where we can use them is in very specific, special places. One is pathfinding:
        let X be the number of steps via the acts-with relation from Kevin Bacon to Jodie Foster;
        let S be the next step via the acts-with relation from Christopher Walken to Kevin Bacon;

And the other, far more powerful place to use relations, is in Descriptions (the Set-Of-Objects type) as a query.
        let L be the list of spiffy people who are friends with Bob;
        repeat with countrymen running through every chatty not boring person who trusts the player begin;
        repeat with countrymen running through every chatty not boring person who trusts the player who is friends with a person (called the shill) who owns a thing (called riches) begin;
                say "Hi [countrymen]; [shill] said you'd lend me your [riches].";
        end repeat;

In the last description, each subordinate phrase applies to the main noun -- person, in this case -- not to the nouns listed in other subordinate phrases -- such as player, thing, or the second person. (Asking "who is friends with a person" is a way of asking who has friends.) We cannot insert commas or conjunctions ("and").


Rules of Thumb


Because interactive fictions are single-author artworks rather than team-designed workhorses, novelty and ease of modification trump safety and scalability. The resulting high-level design decision was for a partially rule-based paradigm over a pure procedural paradigm. Where the procedural paradigm triggers imperative code blocks by invoking that code's given name (the name + code construction is called a procedure or function), the rule-based paradigm triggers imperative code blocks by attaching situational information to it; the situational condition(s) are called the header, and the header + code construction is called a rule.

In other words, a rule encapsulates -when- its code executes. For a procedure, the information about when it executes is spread diffusely over the source code: where-ever its name appears. Rules typically don't need names because rules already know when they execute; they needn't be told so by other code.

In Inform, a colon ends the rule header and begins the imperative code block; a period or a blank line end the block. Articles can be added for readability, and frequently one of the words about/for/of/on/rule can be added as well. (Details in Section 18.6 "The Preamble of a Rule", and Example 370 "Backus-Naur form".)
        A persuasion rule: [...]; [...]; [...].
        Persuasion: [...].
        Every turn: [...].
        Every turn during the collapsing bridge scene: [...].
        Report someone burning something: [...].
        Instead of burning something held: say "No, you might burn yourself!".
        Carry out an actor helping someone: now the noun is friends with the actor.
        Report tattooing: say "You go to work on [the second noun]'s [the limb understood]."

But unlike Prolog's neverending sea of rules, Inform groups its rules into rulebooks. When a rulebook is invoked, only that rulebook's rules are considered. The only required part of a rule's header is the rulebook to which it belongs, though additional clauses are frequently attached using "during" (for scenes), "when/while" (for if-conditions), and a sole action description (such as "someone burning something") immediately following the rulebook name. This allows rulebooks to encapsulate smart, mutable behavior, similar to objects, but behavior here is paramount, not a data instantiation. It could be said that objects implement nouns, while rulebooks implement verbs.

In source code, a rulebook's rules are rarely found together or in any particular order. They can be scattered all over the source code, sprinkled throughout extensions, etc. An author is free to group his rules however he wishes, such as by the narrative scene or geographical area in which they're used, or by the rulebook to which they belong, or, yes, by the object(s) to which they apply.

There are a few cases where naming a rule is useful, but these are usually in addition to the header, not in spite of it. One is in debugging output: the RULES and RULES ALL testing commands list the names of rules as they execute, or are considering execution, respectively. Two is the Procedural rulebook's meta-rules (which are rarely needed). Three is when a hard-coded reference to the rule is required, such as initializing a rule variable, imperatively invoking a particular rule (rarely needed), or listing rules before, after, or instead-of other rules at compile time. Finally, it is always good extension-writing practice to name all rules merely so client code can reference, modify, or delete them, to say nothing of documentation.
        My magic hand rule is listed instead of the can't take distant objects rule in the check taking rulebook.
        The landlubbers can't walk right rule is listed first in the check going rules.
        The block sleeping rule is not listed in any rulebook.
        A procedural rule when the player is a ghost: ignore the can't walk through closed doors rule.



Rulebooks: White-box Paradigm


Unlike a function, a rulebook picks and chooses which rules within itself to execute. It will execute its rules until one of them produces a Success or Failure result, distinct from any (optional) returned value. In lieu of a return statement, rules use one of "rule succeeds", "rule fails", or "make no decision" imperatives, the last of which tells the 'book to try the next rule for a result. If a rule ends without one of these three imperatives, the default imperative -- usually "make no decision" -- is invisibly added at the end of every rule. That default can be set by the rulebook.
        The pick a plan rules are a rulebook.
        The pick a plan rules have default outcome success. [ Or failure, or no outcome ]
        A pick a plan rule: say "I always fail, regardless the rulebook's default."; rule fails.
        A pick a plan rule: say "I can never make up my mind so one of my peers will now execute."; make no decision.
        A pick a plan rule: say "I exhibit the default behaviour for the rulebook."

A rulebook can return an object, a specific named value, or some text. Such named values only have scope within that rulebook. Success/failure can be attached to named outcomes, which saves typing when they stand alone as a statement.
        The audible rules have outcomes silent (failure), whisper (success), voiced (success - the default), shout, and deafening.

        [...]; rule succeeds with result the whisper outcome.
        [...]; whisper.
        [...]; rule succeeds with result my fabulous doodad.
        [...]; rule fails with result "In your dreams."

Though we only need to do so when we make a new one, rulebooks are invoked imperatively. The keywords of note are "consider", "follow", "abide by", and (rarely) "anonymously abide by". The last two also function as a return statement as well, propagating success/failure. Global variables hold all return information.
        consider the pick a plan rules;
        if the rule succeeded, say "How about [the result of the rule]?";
        if the rule failed, say "I've no plans because of [the reason the action failed].";

        follow the pick a plan rules; [invisibly prepends "consider the procedural rules" so meta-rules are re-applied]
        if the rule succeeded, [...]

        abide by the pick a plan rules;
        say "This never prints because Abide By also returns.";

        anonymously abide by the pick a plan rules;
        say "As above, but doesn't clobber the global return variables. Useful for middleman rulebooks."


Rulebooks may also have local variables. Unlike the local variables created by the imperative "let", they remain in scope over all the rules of the 'book, not just a simple imperative block. Though we don't have a specific way of initializing them like Let does (or like an action's locals do (see following section)), we can use the rule ordinal "first" to put a particular rule at the start of a rulebook. (Only "first" and "last" exist this way, and only the final "first" or "last" rule is actually first/last in the book, superseding the preferences of earlier rules that use them.) "Last" rules are used to provide a fallback outcome for a rulebook, such as the Persuasion rulebook always failing.

        The pick a plan rulebook has an object called the best plan so far.
        The first pick a plan rule (this is the initialize plan rule): change the best plan so far to the little-used do nothing rule.
        The last pick a plan rule (this is the default fallback answer rule): rule fails.




Events are Actions


In interactive fiction, the player controls the protagonist, and typed commands translate into actions within the fictional universe. Protagonist actions like Looking, Examining, or Burning have rulebooks attached to deal with those events. Specifically, three rulebooks comprise an Action: its "check" rulebook ensures the action can proceed; its "carry out" rulebook updates game state; and its "report" rulebook narrates the result.
        Check taking something held: say "You already have that!" instead.
        Carry out an actor taking something: now the actor carries the noun.
        Report taking: say "Taken."

Much of I7 programming is writing report rules for actions, though some game events like Every Turn and When Play Begins are also popular. (Game events usually comprise a single rulebook apiece).

Defining a new action is similar to a function prototype of C:
        Donating is an action applying to one thing.
        Discussing is an action applying to one topic.
        Accusing it of is an action applying to one object and one visible object.
        Tabulating is an action applying to one number and requiring light.
        Scheduling is an action applying to one time.
        Temporarily waiting is an action applying to one time period.
        Whining is an action applying to nothing.
        Teleporting to is an action applying to one room.
        Saving the game is an action out of world applying to nothing.
        Tattooing it of is an action applying to one limb and one thing, requiring light.
        Weaving is an action with past participle woven, applying to one thing.

These each create the three rulebooks that will hold the implementation details. Because actions implement an English verb that our player-character (or an NPC) will perform, verbs have a maximum arity of 3 and a minimum arity of 1. The subject, called "the person asked" (or sometimes "the actor"), isn't optional so isn't mentioned above. In the implementation, the direct and indirect objects of a sentence are called "the noun" and then "the second noun". But, if the type of the parameter isn't an object, it will be "the number understood", "the time understood", "the topic understood", and so forth.

The syntax for declaring actions is very special-case: the phrase "requiring light" refers to a worldsim precondition; the adjective "visible" places every object (such as intangible ideas and rumors) in scope, and is completely unrelated to "requiring light"; the word "thing" (or "things") actually means any object(s) -- we can't be more specific and put "person" in there, for example. The phrase "out of world" is for system-level actions; it skips a great deal of the worldsim's calculations. "With past participle X" tells Inform to use that word in source code for the past participle ("has woven") instead of replacing the -ing suffix with the -ed suffix automatically. And finally, other types such as topic, time, time period, number, and various units may each only be used once in an action.

After an action is created, the action must be implemented with rules, and then the parser must be pointed to it via Understand statements. If the parser is not pointed at it, an action can still be invoked from the code, via "try" and "try silently" (or "silently try").
        try Bob donating the jeans;
        silently try donating the red Porsche; ["silently" means the Report rules are skipped. See next section.]
        try Bob accusing the player of theft;
        try teleporting to the tattoo parlor; [subject can be dropped when it's "the player" performing the action]
        try silently the current manager tattooing the back of the player;

For actions with two objects, at least one word (such as a preposition) is needed between the parameters, and the word "it" tells Inform where the first parameter goes. This is useful for actions with multi-part names like "teleporting to it by way of" where the parameter would otherwise have many choices of where it could go.

Finally, an action invocation can be put into a variable of type "stored action" and invoked at a later time. The phrase "the action of" must precede the invocation in order to capture it.
        An abeyance is a stored action that varies.

        [...]; now the abeyance is the action of Bob examining the player;
        [...]; try the abeyance;
        [...]; now abeyance is the action of the current manager firing the noun;

Stored actions are easy to work with when only its nouns are toggled: just use person variables. But they are difficult to deal with when it is the action itself that we want to vary. Extensions have been written to compensate. A built-in To Decide function, "the current action", returns the currently-executing action as a stored action.

(Addendum: very occasionally, the compiler will have difficulty parsing an invocation that has a NPC as a subject. In these cases, add the word "trying" before the action: "Bob trying examining the player". It frequently happens in Tables with a Stored Action column.)



No Lights, No Camera, Just Action


Processing a character's action is a multi-step process, not including parsing. Each step is its own rulebook. In order, (and indenting the NPC-only rulebooks,) those rulebooks are:
Just to be clear, every action has its own Check, Carry out, and Report rulebook. The other rulebooks are shared among all actions.

Each rulebook has its purpose:
All rules have an arity from 1 to 3, corresponding to the subject, direct object, and indirect object of a simple English sentence. The subject is always present, because it is the character performing the action. But a rule may apply to a specific character, to all NPCs, to only the PC, or to everyone:

        Instead of Tiny jumping: say "Tiny is too overweight to jump. You all must find another way to help him across."
        Carry out an actor jumping: now the actor is on the nearby platform. ["an actor" applies to everyone]
        Report someone jumping: say "You see [the actor] jump over." ["someone" applies to any NPC]
        Report jumping: say "You jump over." [subject-less means the player]

(Games with multiple PCs should note that rules specific to one character (such as "Tiny jumping") will not fire if that person is the PC. Only the subject-less rules will fire.)

The "setting action variables" rulebook initializes its action's local variables by pulling values from global variables and object properties, including the parameters "the actor", "the noun" and "the second noun". (It is NOT a way to pass additional parameters to a rule.) The "matched as" parenthetical is optional, but it allows rules in the rest of the gauntlet to test those variables similarly to how it tests on the actual parameters. The standard Going <direction%gt; action uses this to provide rule hooks like Going To <a room%gt;, Going Through <a door%gt;, Going By <a vehicle%gt;, etc.
        Report going from the monkey village to the lost city's entrance by the hoverboard: say "You sail into the clearing easily this time, bypassing all those nasty monkeys that like to drop on top of you."
        The jumping action has an object called the obstacle (matched as "over"). [must be a single word!]
        Setting action variables for jumping:
                now the obstacle is the current blockage of the location. [pulls data from the "current blockage" property of a room; specifically, the room the PC is in at that moment]
        Instead of Stevie Burns jumping: say "Even little Stevie hops over [the obstacle]." [ Jumping takes no parameters beyond the performing actor ]
        Instead of Stevie Burns jumping over a fiery fallen beam: say "'Help!' says Stevie. You suddenly recall him telling you about the time his house burned down." [ we can now test on the local variable with an "over" phrase ]

Because the Before, After, and Instead rulebooks are shared among all actions, they may apply to entire categories of actions. A named group of actions is called a "kind of action", and they have no special declaration syntax beyond the word "is" sitting between the action's name and the kind-of-action's name. This encourages all manner of nouns, adjectives, and adverbs to name a kind-of-action, but some read better than others when used in rule headers. In all cases, an action must be defined before attempting to classify it.

        Whining is pointless behavior.
        Temporarily waiting is pointless behavior.
        Discussing is conversation.
        Accusing it of is conversation.
        Accusing it of is drama.
        Teleporting to is drama.
        Tabulating is acting like a frickin' accountant.
        Scheduling is acting like a frickin' accountant.

Kinds of actions can then be used in rule headers. (In the following examples, remember than "when" and "during" begin additional clauses that check global variables and whatnot.)

        After pointless behavior: say "But you still feel unfulfilled."
        Before conversation when the current interlocutor is not in the location: now the current interlocutor is a random person in the location.
        Instead of drama, say "(Now is the time to lay low!)"
        Instead of acting like a frickin' accountant during the collapsing bridge scene, say "You calculate (correctly) that you're about to become a victim of natural selection."

Any rulebook that can accept a kind-of-action can accept an explicit list of actions as well. Let's then call this list Feature #1, because of the many variations on it. There is a special kind-of-action, the noun phrase "doing something/anything", that matches all actions. (Feature #2). Optionally, it may [#3] be followed by an "except" / "other than" clause that lists actions. Then, optionally, we may [4] tack on an object Description to further constrain the rule. (The Description can [5] be preceded with "with/to" if we wish; it frequently improves readability.) And of course, we may [6] always add other conditions with "when" and "during", such as the "in the presence of Person" boolean function.

        Before an actor discussing [4] a spiffy thing [6] when in the presence of Mr Blackheart: [...].
        Instead of [2] doing anything [3] except waiting [6] when the player is paralyzed, say "(Uhh... can't... move...)"
        Instead of someone [2] doing anything [3] except taking, dropping, or burning [5] with [4] something incriminating, say "[The actor] says, 'No, I must get rid of [the noun]!'"
        After [1] examining, looking under, or searching [4] anything owned by Mr Blackheart [6] during a scene needing tension: say "Suddenly, Blackheart re-enters the room. 'What are you doing.' It wasn't a question."

Just to be clear, "doing something" is a single identifier. "Doing" is not an action, and its following "something" is not a parameter Description. But "something" is a parameter Description elsewhere, as in "examining something" or "accusing something of something".

Six caveats: One, when using "doing something", we must remember that the Looking action is what primarily prints text when we enter a place -- or re-prints it when we load the game, etc. -- so we frequently will want to allow it. Two, when using "doing something to/with/-- something", actions of low arity like Sleeping and Waiting won't be caught by it, because they never apply "to/with something". Three, the docs don't cover any syntax for constraints on "the second noun", but a "when" clause can be appended. Four, when explicitly listing several actions together, they must check the same arity. (So we can't combine "sleeping" with "examining something", but we can combine "sleeping" and "examining". The arity of the check, not of the action, is important.) Five, but we can combine them using a kind of action (see immediately below). And six: in all cases, the subject of the actions is kept completely separate from the actions' description; consider it already processed by the time the action group's description starts.

        Examining something is acting like a klutz. [not "an actor examining something"]
        Dropping someone is acting like a klutz. [notice the "someone"]
        Looking is acting like a klutz. [arity of the check is lesser than the other two]

        Before someone acting like a klutz: [...]. [works for any NPC examining a Thing, dropping a Person, or Looking]



Understanding Our Player, Our Parser


The player's parser is a fairly simplistic one, little changed over a decade of use, but it makes heavy use of callbacks for some surprising extensibility. These parser callbacks are called topics (meaning, they are of type Topic). Topics are simplistic regexes in the shape of a boolean function, and if they match, they "return" what object, unit, action, or value they find by setting global variables. A given topic must "return" only one particular type; more on that in a moment. We use Understand sentences to add these topics to the pre-existing list of other topics that the parser runs down. Here are some examples of using Understand to connect topics with actions:
        Understand "whine" as whining.
        Understand "donate [something]" as donating.
        Understand "give away [something]" as donating. [we're making a synonym here]
        Understand "discuss [text]" or "talk about [text]" as discussing. [ditto, but as one topic not two]
        Understand "tabulate [a number]" as tabulating.
        Understand "answer [truth state]" as answering.
        Understand "schedule [a time]" as scheduling.
        Understand "wait for [a time period]" as temporarily waiting.
        Understand "teleport to/-- [any room]" as teleporting to. [the altercation slash can't be used on the first word]
        Understand "accuse [someone] of committing/-- [any thing]" as accusing it of.
        Understand "[any thing] committed by/via [someone]" as accusing it of (with nouns reversed).
        Understand "wear [something preferably held]" as wearing.
        Understand "put [other things] in/inside/into [something]" as inserting it into.
        Understand "deposit [something] in/into [an open container]" as inserting it into.
        Understand "go to [any adjacent visited room]" as going by name.
        Understand "tattoo [limb] of [someone]" or "tattoo [specific limb] of [someone]" as tattooing it of.

The whole text between "Understand" and "As" is a topic, and the text in the square brackets is yet another topic called by the larger topic. The called topics start their parsing where the calling topic left off, trying to decide if the words that follow match itself.

The topic "any thing" -- as opposed to "thing" or "something" -- ignores the limitations of scope, and will match any valid object found in the whole game. The word "any" in general works like this, and must be echoed in the action definition by the word "visible". You can then guess what "any room" means. Other topics are intended for a particular type -- Time, Number, Units, named values (like Limb), undigested text (which the actions reference by "applying to one topic"), etc. One of the types, Description, is quite powerful: "an open container", "any adjacent visited room", and "something related by reversed containment" are examples of descriptions.

Understand can also expose other parts of our games to our players: property adjectives, synonyms for object names, and even other topics:
        Understand "dog" as Rover.
        Understand "birds" and "ruddy ducks" as the plural of duck.
        Understand "upper [limb]" or "lower [limb]" as "[specific limb]". [this won't capture the words Upper or Lower, but allows the player to use them ]
        Understand "beneath/under/by/near/beside/alongside/against" or "next to" or "in front of" as "[nearby]". [this is a convenience for the programmer only]

Any single topic can only return one specific type of something
        Understand "colour [a colour]" or "[something]" as "[tint]". [error: is this Topic's return type a Color or Thing?]

        A pot is a kind of thing. A pot can be broken or unbroken.
        Understand the unbroken property as referring to the pot. [or, "...the broken property..."]
        Understand "shattered" or "cracked" or "smashed" as broken.
        Understand "pristine" as unbroken.

        Understand the broken property as describing a flowerpot. [describing disallows "take broken"; player must provide the noun: "taken broken pot"]

        Understand "bottle of [something related by containment]" as a bottle.
        Understand "machine" as a device. [device is a class, so "machine" will narrow down the choices if the parser must make a guess]

Finally, if-conditions can be attached to any of these Understand statements, so the parser will ignore the line if its condition isn't met.
        Understand "rouge" as red when the make-up set is visible.
        Understand "Rover" as Rover The Dog when the player knows-about Rover.
        Understand "your" as a thing when the item described is held by the person asked.

But remember that this happens during parsing of the player's command, so the condition can't always refer to information within the command itself, such as "the noun" or "the second noun". In the case of "the person asked", it works for FRODO, GIVE ME YOUR RING because Frodo has been parsed already; but it may not work if "Your" is the very first word of the command.

If we just want a simple reply with no processing, there's a shorthand.
        Understand "xyzzy" as a mistake ("The machine doesn't seem to have a button with that label on it.") when in the teleportation chamber.
        Understand "xyzzy" as a mistake ("Ah, I see you're an old hand at this.").

Mistakes can use the [text] token but little else. They can use To Say functions in what they print, so it is possible to do some simple codework from them. Mistake lines are parsed before non-mistake lines, because they're intended to cover exceptions to grammar or one-off, out-of-game remarks by the player, rather than entire categories of input.



Time for a Scene


The Room class divides space into discrete places. Scenes divide an interactive fiction into durations of time. Each scene is implemented as a boolean variable that is automatically set & cleared by a condition. Scenes may happen again unless declared "non-recurring".
        A lightsaber duel is a [non-recurring?] scene.
        A lightsaber duel begins when the location of Luke is the location of Darth.
        A lightsaber duel ends when Luke is too injured to continue or Darth is too injured to continue.

        A person can be too injured to continue. A person is rarely too injured to continue.

Rule headers and imperative code both can check to see if a scene is happening.
        Every turn during a lightsaber duel: say "BWWAAUUAAAHH".

        [...]; if a lightsaber duel is happening, [...]

Each scene provides two rules that execute when the scene begins and ends. These are just hooks we may use or ignore.
        When a lightsaber duel begins: change the command prompt to the battle command prompt.
        When a lightsaber duel ends: change the command prompt to the normal command prompt.

Scenes are not objects, but we can add boolean properties to them in the same way.
        A scene can be thrilling or dull. Train Stop is dull.
        A scene has a text called cue speech. The cue speech of Train Stop is "All aboard!".
        Every turn during a dull scene: [...].
        [...]; if a thrilling scene is happening, [...]



Arrays Have Been Tabled


Inform is not a language of computation. It is a language designed to hold secrets for the player to discover. So its two-dimensional arrays -- tables -- are primarily intended for a single, occasional lookup. Tables are statically-allocated like everything else, and the words "row" and "column" are used a lot in lieu of (x,y) coordinates.

Here's an example table from the docs:

Table 2.1 - Selected Elements
Element (some text)    Symbol (some text)    Atomic number (a number)    Atomic weight (a number)   
"Hydrogen" "H" 1 1
"Iron" "Fe" 26 56
"Zinc" "Zn" 30 65
"Uranium" "U" 92 238

Or, alternately:

Table of Selected Elements
Element    Symbol     Atomic number    Atomic weight   
text text number number
"Hydrogen"      "H" 1 1
"Iron" "Fe" 26 56

A blank line and the keyword "Table" begin the whole affair. At least one tab character is required to end a column and start the next. Multiple tabs and spaces are OK; the first tab character Inform comes across melds together all whitespace surrounding it. Type information can either be left off -- Inform will deduce it mostly correctly -- or can be given in the two styles shown. Both styles have problems. The first, "inline" style only allows "object" for objects, not a particular class (like "person"). The in-row style allows specific class names, but introduces a bug -- row #1 will be a blank row that doesn't correctly flag itself as blank. This is because we can:

Table of Energy Proponents
Proponent     Fuel    Danger
Bob Hydrogen a number
Phoebe Wind --
-- Geothermal      --
Jean Nuclear 10
with 4 blank rows.

..only put the type in-row when it would be blank otherwise (and defeat the inference). A blank entry is denoted by the -- double-dash. The last line ("with X blank rows") statically allocates some extra space. A character's responses to the player's inquiries on various topics has special syntax, a column entitled "topic" whose type will also be Topic. Remember that topics are (simplistic) regexes, so they can't be printed.

Table of NPC Responses
topic answer
"hi/hello" "He says, 'Well hello there!'"
"bye/goodbye"    "'Take care,' he answers."

There's several phrases to work with tables, but it's still one of the more cumbersome parts of the language. This is because we must "choose" a particular row and then work with each "(column name) entry" as if they were each a standalone variable. We can't pass a chosen row to another function. We can't choose two rows simultaneously, either in the same table or in different tables. We can't copy rows to other rows. We can't look up a row based on multiple criteria except by a laborious explicit loop with O(n) running time and hard-coded queries. Finally, we can't create N-dimensional arrays except by creating a column of type "table-name" and the creation of a lot of statically-allocated sub-tables by hand!

Rather than explaining every detail of the procedures and functions related to tables, I'll list most of the examples from the docs (Chapter 15), and capitalize the names of the parameters. Their operation should be relatively obvious. The following are all imperative.
        if Wind is a Fuel listed in The Table Of Energy Proponents ...
        blank out the whole row;
        choose row My Favorite Number in The Table Of Energy Proponents;
        if there is no Proponent entry ...
        choose row with Danger of 10 in The Table Of Energy Proponents;
        if there is a Proponent corresponding to a Fuel of Geothermal in The Table Of Energy Proponents ...
        choose a blank row in Table 2.1;
        change Element entry to "Fluorine";
        change Atomic Number entry to 9;
        if there is an Atomic Number in row 2 of The Table Of Standard Elements ...
        sort The Table Of Energy Proponents in reverse Danger order;
        sort The Table Of Energy Proponents in random order;

These are value-returning functions:
        ... the number of blank rows in Table 2.1 ...
        ... the number of filled rows in Table 2.1 ...
        ... the Proponent corresponding to a Fuel of Hydrogen in The Table Of Energy Proponents ...
        ... Symbol in row 3 of The Table Of Selected Elements ..

And these are examples of putting a table lookup in a rule header; the table-specific part begins with a column name, followed by "listed in", and then a table name:
        after taking An Item listed in The Table Of Treasures: [...].
        after asking The Guru about A Topic listed in The Table Of NPC Responses: [...].

One concession to ease of use is that its sort routine is stable: if multiple rows have the same value in the column we sort on, then their order vis-a-vis each other will not change.



Named Values Everywhere


Inform uses named values everywhere; it is usually properties that remain anonymous. We can explicitly create a new named value:
        A limb is a kind of value. The limbs are left leg, left arm, right leg, right arm, the neck, and the back.
        Tattoos are a kind of value. The tattoos are mom, barbed wire, wings, a kanji, a pseudo-photograph, and some obscure symbol.

Named values can have spaces in their names, but they cannot have their names abbreviated like objects can. Inform generally allows almost any word to be part of a named value, but be warned, if you put words like "when" or "is" in a named value, it may create compilation problems when used in other, particular places.

Named values are a type, similar to Time or Number but finite in length, that can be used in many places.
        change X to the limb after the left leg;
        change X to the limb before the back;
        Definition: a limb is hurt if [...].
        Tattooing is an action applying to one limb and one thing.
        Understand "tattoo [limb] of [someone]" as tattooing.
        Exposition relates various people to various tattoos. The verb to be tattooed with implies the exposition relation.
        Bob is tattooed with some obscure symbol.
        [...]; repeat with X running through each limb begin;

In the above, the action "tattooing", the relation "is/are tattooed with", and the named value "tattoos" are all distinct from one another.

Named values can also be given properties, as an object can.
        

Named values can be defined by a table. Since tables can be continued and amended by client code at compile-time, this is a handy way to leave our named values "open".
        Tattoos are a kind of value. Some tattoos are defined by the Table of Designs.
        
        Table of Designs
        tattoo
        barbed wire
        wings

        Table of Designs (continued)
        tattoo
        Jean-Pierre 4-ever

Inform automatically creates some named values that correspond to other parts of the language. For example, "the containment relation" is an example of the "abstract-relation" named value, and corresponds to a built-in relation. If we need to pass a relation to the pathfinding function, it is the named value we are passing. Our own relations would be treated similarly: "the marriage relation". Likewise, "action-name" is the umbrella for "the looking action", "the accusing it of action", etc.
        Causality relates an action-name (called the cause) to an action-name (called the effect) when [...].
        SecondOrderRelation relates an abstract-relation (called the scaffold) to an action-name (called the side effect) when [...].



T.E.X.T.


Though it's probably obvious by now, text is put between double quotes; within, text in square brackets is a To Say phrase (a procedure call). Where other languages use the word print, Inform uses the word say. Punctuation note: the ' apostrophe expands to a double quote, except for standard verb contractions like don't or can't. Otherwise, substitutions are frequently required for certain punctuation. So to print:
        Rene Descartes said, "I think not!" and promptly disappeared.
        So don't ever say that, 'k?

Use:
        say "Rene Descartes said, 'I think not!' and promptly disappeared.[line break]So don't ever say that, [']k?";

Variables and objects can be put in the square brackets to print their current value and their name, respectively. (The outputted name of an object can be overridden by a rule in the Printing The Name Of Something activity; more on activities later.) Line breaks are automatically appended if the last character is a period, question mark, or exclamation mark.

Inform was originally designed to make games with a very small memory footprint, so a game's output text is compressed, and language support for regexes is poor. The Text datatype is this compressed text. Indexed Text -- a later and still optional addition to the language -- isn't compressed, so regex operations will work on it. Typecasting between the two is mostly automatic and is one of the very few (if not only) cases of implicit casting. Also, both text types used the double quotes identically, so constructions like the following are needed to avoid creating a plain Text type by accident:
        let T be indexed text;
        let T be "Hello World";

Typically, access to input text isn't direct. Modification is mediated through the Snippet type, the Topic type, and a couple of phrases. A snippet is a pair of numbers, joined together like fixed-point notation, that represent a range of words. "The 3 words starting at word #2" would be the snippet 203. There are only three built-in snippet variables, "the player's command", "the matched text", and the confusingly-named "the topic understood".
        if the player's command does not include "please/thanks", say "How rude!";
        if the player's command includes "please/thanks", cut the matched text;
        if the player's command includes "hi", replace the matched text with "hello";
        if the player's command matches "hello world", say "You're a programmer, aren't you?";
        if the player's command does not match "hello world", say "To code... perchance, to write?";

Topics are simple textual patterns, a poor man's regex, that attempt to match input text. They are also written simply with double-quotes, and their only operator is the / slash as high-precedence altercation. In the above example, "please/thanks" is a topic (the type was inferred from the "include/includes" function). Topics are frequently used in a game's conversation system, where implementing as objects everything that's interesting to ask about would eat memory. Topics are also why the object hierarchy does not have an Idea or Concept class, since variables can track any information gained that isn't implicit in the owning of a physical object. We'll see more of Topics, and the peculiar snippet "the topic understood", when we examine tables.

Indexed text is a late addition to Inform, and is cumbersome to deal with because the language tries to remain statically-allocated. As a result, our string-manipulation type has a lot of restrictions on where and how it can appear. But it is finally possible to gain direct access to the player's input by typecasting the snippet to indexed text:
        let N be indexed text;
        let N be the player's command;
        [...];
        change the text of the player's command to N;

Know that the last line is a very special one-off syntax designed to blend in with the rest of the language -- even the word "the" is required. Snippets do not have properties, other snippets cannot be used in that line, and there's only one parameter there -- N, in this case -- not two. We won't cover the regex engine here -- see Chapter 19 "Advanced Text" -- except to say it executes very slowly.



Precisely One Spoon-unit Of Sugar


Units are user-defined numeric types with some nifty syntactic sugar, sugar that extends even to our player. Generally in programming, numbers are numbers: the programmer must remember one variable measures in pixels while another measures in picos, and to mix the two makes bad medicine. But Inform allows us to define unique ways of writing the numbers -- by wrapping identifiers and punctuation marks (the "preamble") around them. The individual numbers ("parts") in the construction can be given names for later referencing.

        Money is a kind of value. $19.99 specifies some money with parts dollars (without leading zeros) and cents (optional, preamble optional).
        My wallet is some money that varies. My wallet is usually $20.75.
        A thing has some money called the price. The price of a thing is usually $5.
        The price of Bob is $2.05.

        say "[the dollars part of the price of Bob]";

We can only add and subtract units, assuming the units match. To allow multiplication and division, we can specify what new unit is created. Note the re-use of the specify/specifies verb.
        A length is a kind of value. 10 m specifies a length.
        An area is a kind of value. 10 sq m specifies an area.
        A length times a length specifies an area.

We can always multiply and divide a unit by a plain number. Multiplying and dividing by one is how we explicitly typecast between units and numbers.

Inform automatically creates a new Understand token from units. The player writes the unit the same way(s) as the programmer.
        Understand "donate [money]" as donating. Donating is an action applying to some money.

Finally, we can re-use the "implies" statement from defining relations for an extra dose of sugar.
        The verb to cost (it costs, they cost, it is costing) implies the price property.
        The jeans cost $19.95.

Note that we didn't define a relation there, though it may look like it. But we could:
        Fanciness relates a thing (called X) to some money (called Y) when the price of X > Y. The verb to be fancier than implies the fanciness relation.

        let L be the list of things fancier than $2.50;
        let B be the list of things fancier than the price of jeans;

The built-in type Time is not a unit, but pretends to be. Time's parts -- hours and minutes -- are calculated from a 4 AM minute-count, not stored as separate numbers as units are done.



Backstage Activities


Actions are "on stage" because our player sees the character(s) performing them. But sometimes backstage jobs need to get done. Inform is definitely not concurrent, so "backstage" isn't meant in the way that "background UNIX utility" is meant. It's higher-level than that. Anything that utilizes three rulebooks, and all the machinery that that entails, isn't going to be overriding addition nor running every tenth of a second. Rather, activities are used for tasks such as AI, prose generation, parsing, complex decision-making, etc., and frequently it's useful to have hooks before and after such tasks, in case client code wants to shuffle things around just before the task, and restore them just after. Also, rulebooks & phrases can ask if they're being used in the context of an activity.
        Rule for printing the name of the sack while the sack is not carried: say "your abandoned sack".

        [...]; if the printing the name activity is going on, [...]

Though we occasionally need to add a rule to a pre-existing activity to get it to print what we want, it's rare that they need be extensively modified or directly invoked. Generally, adding a rule to an activity is "hacking lite".

The built-in activities are listed in Chapter 17; just scan the headings from section 17.9 on down. Grouped roughly by task and order, they are:

System-level:
At or near the beginning of a new game:
        Starting the virtual machine
        Constructing the status line
        Printing the banner text
At the end of the game:
        Printing the player's obituary
        Amusing a victorious player

Parsing Input:
Reading a command ("After reading a command" is a popular hook for pre-processors because it happens before the rest of these)
Deciding whether all includes
(the player's keyword ALL causes problems; (please don't Take the Sun))
Deciding the scope of something
(for when the visibility and accessability rules miss a case; this is called a lot by the system)
Deciding the concealed possessions of something
(similar to scope, but not so heavy-handed; this is also called a lot)
Clarifying the parser's choice of something
(when the parser takes a guess at what the player meant, it tells you)
Asking which do you mean
(when the parser doesn't want to take a guess, it asks you; "Which X did you mean?")
Supplying a missing noun
(when the author takes a guess at what you meant)
Supplying a missing second noun
(ditto)
Printing a parser error
(all of them are listed on its doc page)

Sensory:
        The all-important Looking Action, in chronological order and indented according to caller/callee:
         Printing room description details of something (or are we in Darkness? If so, skip down)
         Printing the locale description of something
(of the room, then of the cage we're in, etc.)
         Choosing notable locale objects for something
(what's in here? what's important and needs describing first?)
         Printing a locale paragraph about
(which doesn't actually print anything, just demotes or promotes things)
         Writing a paragraph about
(actually prints)
         Listing nondescript items of something
(items lacking descriptive prose are just listed at the end, via the Listing Contents Of activity)
        Darkness, when Looking doesn't work
         Printing the announcement of darkness (when you flick the light switch, this happens)
         Printing the name of a dark room
("the dark" is implemented as a room by itself, not as a condition in an existing room)
         Printing the description of a dark room
         Printing a refusal to act in the dark
(almost all standard actions "require light" -- even when they're conversation!)
         Printing the announcement of light

Reasoning:
Implicitly taking something (usually, we allow the system to save the player the step of Taking something before using it)
(oddly, the built-in pathfinding is not an activity)

Output Prose Generation:
of a particular thing:
        Printing the name of something
        Printing the plural name of something
        Printing a number of something
of a list of contents:
        Listing contents of something (pretty-prints a list of objects "held" by the sole object passed to it; used by the Inventory action, the Looking action (for the nondescript items), and saying "[contents of...]")
        Grouping together something
(utilized by the above to offer some control on the order)



Testing Commands


We have one testing command in the language itself:
        Test me with " look / test foobar / wave ".
        Test foobar with " look / take me / jump ".

This will execute the slash-separated list of runtime commands when the TEST runtime command is issued. "Me" is, by convention, the usual name for a script, and extension examples are encourage to name their overarching test script "me".

We have several run-time testing commands at our disposal:

The IDE has the Skein -- which is a tree of commands of every playthrough we've done in the IDE -- and the Transcript -- which is a detail view of one path in the Skein; it has the output text from those commands. Transcript also has the Bless button, so automated re-testing of changes is possible.



Indistinguishable Memory


In spite of Inform's wish to remain statically-allocated, it has several aggregates that just won't allow themselves to remain so. Lists, Indexed Text, and Stored Actions all conspire to work against it. Inform tries very hard to uphold its slogan of "one pointer, one block", For example, it will tend to make deep copies of anything that is passed to a function. However, the underlying virtual machine does allow dynamic allocation, and extensions have been written to expose this.

Inform does have a special syntax for creating up to 100 indistinguishable instantiations of things:
        A coin is a kind of thing.
        55 coins are in the couch. 3 coins are carried by the player. There are 7 coins. [the last definition creates objects without putting them anywhere]

There's many things to know about this. One, it only works on kinds (classes), not pre-existing instantiations, so creating a subclass for the purpose is usually needed. Two, the subclass must be defined before the multi-instantiation; else we'll have a single object called "55 coins". Three, unlike an array of objects, neither the programmer nor the player can specify any particular instance, such as "the thirty-second coin". Four, the programmer can't specify a quantity of them, as in "move 3 coins to the recliner" or "try taking three coins" because Inform will say "which 3 coins?". (As a general rule, Inform does not allow the programmer to specify several things for an action.) Five, the player can "TAKE THREE COINS" if he spells-out the number, and the action's Understand line applies to [things], as the Taking action is already declared to be.

Six, the programmer can specify one particular coin from the bunch based on its properties and situation. This is where the Description type is essential. Since we're subclassing anyway, we can give coins additional properties, such as "tarnished", or a named value like "metal", and then use the word "random" to pick one while observing those constraints:
        try taking a random tarnished coin which is carried by the player;

However, this can't type-check that such a coin exists. And finally, the property adjectives can be expressed to the player's parser to give the player a similar ability to distinguish between indistinguishable objects:
        Understand the tarnished property as describing a coin. [tarnished by itself won't grab a coin]
        Understand the metal property as referring to a coin. [but copper will]

Though we wouldn't normally expect the player's parser to be more expressive than the programmer's, in the case of taking three coins, the runtime always has the option of interactively asking for details ("Which did you mean?") while the programmer's does not.



Grammar Gotchas





Paradigm Leaning


Given its hybrid nature, we must watch ourselves when we code algorithms. Sometimes Inform's declarative features are more elegant than doing the same work imperatively, but other times, its domain-specific nature frequently prevents higher-order programming algorithms. Here are a few examples that, like GOTO, probably signal a "wrong way" to do something.




To Hack... or not... to Hack?


Inform has several ways that deeper changes can be effected into the language.



This is only most of Inform 7. It is a large language with many nooks and crannies to explore. Finding them is frequently a game in itself. We hope you find it enjoyable.



1