Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

The difference between IF and general purpose languages?

18 views
Skip to first unread message

JC

unread,
Mar 25, 1998, 3:00:00 AM3/25/98
to

If special purpose Infocom-like IF languages such as Hugo, Inform and TADS
did not exist we would have to use general purpose languages, such as C,
Scheme or Oberon. In this case, libraries could be written to take much of
the programming burden. For example, you could call a procedure from a
paser module to parse the player's command. How different this task would
be from using something like TADS I'm not sure.

One thing is certain though. In a general purpose language the code for a
game wouldn't as succinct (sp?) or short (even if making procedure calls to
IF libraries). By making a language domain specific you can tailor the
syntax and semantics to the domain and hide away all the "house keeping"
details in the language itself. A functional language is far better for
solving mathematical problems than assembly language, because a functional
language allows the problems to be expressed in a natural way; with
assembly a lot of the programmer's mental power is being used to do things
not directly related to solving the problem. This "suitability" [I wish I
could think of a better term] isn't just restricted to a small set of
levels, like assembly vs. high-level languages, either; there is a
continuous range; if there is slightly less mental load on the programmer
then they can use more of their brain for important details, to do more
powerful things with the language, for example.

I'm interested in knowing what things would/do make an IF language more
suitable for Interactive Fiction. If you could answer this question
perfectly you could create the perfect tool for creating IF. As a small
start, I've been trying to answer the question "What are the differences
between current IF languages and general purpose ones?" As I only know
TADS well, the following is what I could think of for it. I don't think it
is exhaustive.


- Support for typical IF commands, such as saving and loading games, and
undo.

- Built in player command parser.
- Commands can take the form of "verb", "verb directObject" or "verb
directObject preposition indirectObject".
- The concept of verbs and vocabulary words is hard-coded into the
language. This has several aspects:
- Verb objects have a "verb" property, which lists the words which
the player can type in to represent the verb. It may look

something like this:

cleanVerb: Verb
verb = 'clean' 'polish'
....
;

- Objects which can be acted upon with a particular verb, e.g. a
widow can be opened (the open verb), must define methods which
verify and carry out the verb action upon it. So the code for
a window may look as follows:

window: Item
...
verDoOpen(actor) = {
if (self.isOpen) "The window is already open";
}
doOpen(actor) = {
"You open the window";
self.isOpen := true;
}
...
;

The name of these methods depends on the doAction property in the
verb. In this case, the doAction property in the OpenVerb would be
= 'Open'.

For verbs which take an indirect object there is a similar set of
properties/methods, but this time spread out between the verb,
direct and indirect objects.

- Actions can be directed to actors, and format strings are used to
tailor the resulting text to that actor.

- Disambiguation of an ambiguous reference to an object in commands,
to make an intellegent guess at what the player meant.

- For direct object verbs, default direct objects. For direct and
indirect object verbs, default prepositions and indirect objects.

- Equivalent objects supported by "isEquivalent" property.

- Support for abbreviations.

- Support for pronouns.

- Multiple objects can be used in place of a direct object, where the
objects can be an explicit list of objects, the word "all", or "all"
qualified "but"/"except" followed by the objects to exclude [does this
have to be an explicit list??].

- Various functions can be defined by the author to customise the
parsing process.

- Concept of compound words, special words and prepositions.


- Text enclosed in double quotes (") automatically displayed upon
evaluation, rather than having to be passed as a parameter to a function.

- Mix between procedural and declarative style, in the sense that coding
mainly involves declaring objects which appear in the game, and doesn't
involve writing code for stuff like the input loop; most of this sort of
stuff is part of the language. To quote the manual "..TADS programs
usually have procedural sections, in which steps are executed in
sequence, the overall program doesn't have a beginning or an end".

- Weak typing. From the manual: "..you don't have to declare in advance
the datatypes of your variables, functions, and properties".
To illustrate the usefulness of properties being either data or methods,
consider the following situation. When the player types "west" to cross
a bridge, in which case the property "west" in the object for their
location is evaluated to return the room to their west. The code for
this might look like the following:

eastSideOfChasm: Room
...
west = westSideOfChasm
...
;


but say the author wanted to display a message about the bridge being
creaky as the player walks across it. In this case, they could do the
following:

eastSideOfChasm: Room
...
west = {
"The bridge creaks as you walk across it.";
return(westSideOfChasm);
}
;


The advantage here seems to be that the former of these two forms is like

a shorthand for
west = { return(westSideOfChasm); }

Are there other advantages or disadvantages of this?

The weak typing also acts like polymorphism, as parameters and lists can
take objects of any class(es).

- Turn based gameplay hard-coded into the system, with explicit support for

daemons and fuses.

- Object Oriented Sytem.
In TADS there is little difference between a class and intances of the
class. Rather than classes being like just a template for the
"structure" of its instances, the properties of classes can have values.

For example, in WorldClass the button class is as follows:

class Button: Thing
noun = 'button'
plural = 'buttons'
sdesc = "button"
touchpress = true
verDoPush(actor) = {}
verDoTouch(actor) = {
if (self.touchpress)
self.verDoPush(actor);
else
inherited.verDoTouch(actor);
}
doTouch(actor) = {
if (self.touchpress)
self.doPush(actor);
else
inherited.doTouch(actor);
}
;

I guess this allows a more consise, declarative style; you don't
have to imperatively initialise property values. Could this
be more suitable for IF because IF classes tend to more-often represent
actual physical things? What other advantages and disadvantages are
there to this?

TADS also uses Multiple Inheritance, which could be used to make a glass

box both an 'openable' and 'seethrough' item.

- Lisp-like lists. Lists are much easier to handle in TADS than in a lot
of languages. Like some of the other features, there is a preference of
ease of use over performance.

Features usually/often found in general purpose languages but not in TADS:

- Pointers.
- Floating point numbers.
- Full IO system.
- Real-time support (I understand you could do limited real-time stuff with

calls to another language).
- Support for information hiding and encapsulation.

Misc:

- Refering to a property that hasn't been declared creates the property
with a value of nil. I don't understand the purpose of this.
- No global variables (but objects are global).


I'm sure there's more than this. I haven't had that much experience in
using TADS, so I'd be interested in any comments from those who have. Are
there any little things I've missed out? any big things? Are there any
things I've given less importance to than they deserve? Have I looked at
this from the right perspective?

Hang on, I know a better way of doing this... I have given an extensive
list of exactly what differences there are between TADS and a general
purpose language. I haven't missed any details or made any mistakes.
:-)

I guess another way of approaching this is: when coding your game, were
they any particular aspects which the IF language was particularly well
suited to coding?

For the other IF languages, what features do and don't they have, and why?


';';James';';

John W Kennedy

unread,
Mar 25, 1998, 3:00:00 AM3/25/98
to

JC wrote:
> I'm interested in knowing what things would/do make an IF language more
> suitable for Interactive Fiction.

I think the only important parts for the _language_ are:
a built-in lexer,
"soft" objects that either allow testing an unknown object to know
whether it has such-and-such a method or not, or else have have a benign
result when an unprovided method is invoked, and
convenient access to packed single-bit and single-byte object
attributes.

Everything else belongs in a library.


Andrew Plotkin

unread,
Mar 25, 1998, 3:00:00 AM3/25/98
to

John W Kennedy (jwk...@ibm.net) wrote:

> JC wrote:
> > I'm interested in knowing what things would/do make an IF language more
> > suitable for Interactive Fiction.

> I think the only important parts for the _language_ are:


> a built-in lexer,
> "soft" objects that either allow testing an unknown object to know
> whether it has such-and-such a method or not, or else have have a benign
> result when an unprovided method is invoked

> convenient access to packed single-bit and single-byte object
> attributes.

I agree. Note that are language features, *not* virtual machine features.
In particular, the lexer should not be hardwired in the VM (assuming
you're using a VM model at all) and the object structure should be as
little as possible, possibly not at all.

> Everything else belongs in a library.

On the other hand, having the lexer in the language is the *same* as
putting it in a library. Possibly a library that you can't avoid linking.
Might as well have the choice, I say.

--Z

--

"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the
borogoves..."

JC

unread,
Mar 25, 1998, 3:00:00 AM3/25/98
to

On Wed, 25 Mar 1998 12:38:55 -0500, John W Kennedy <jwk...@ibm.net> wrote:

>I think the only important parts for the _language_ are:
> a built-in lexer,
> "soft" objects that either allow testing an unknown object to know
>whether it has such-and-such a method or not, or else have have a benign

>result when an unprovided method is invoked, and


> convenient access to packed single-bit and single-byte object
>attributes.
>

>Everything else belongs in a library.

Hi John,

Could you elaborate on why you think this?


';';James';';

Jeff Hatch

unread,
Mar 25, 1998, 3:00:00 AM3/25/98
to

JC wrote:
> One thing is certain though. In a general purpose language the code for a
> game wouldn't as succinct (sp?) or short (even if making procedure calls to
> IF libraries). By making a language domain specific you can tailor the
> syntax and semantics to the domain and hide away all the "house keeping"
> details in the language itself. A functional language is far better for
> solving mathematical problems than assembly language, because a functional
> language allows the problems to be expressed in a natural way; with
> assembly a lot of the programmer's mental power is being used to do things
> not directly related to solving the problem. This "suitability" [I wish I
> could think of a better term] isn't just restricted to a small set of
> levels, like assembly vs. high-level languages, either; there is a
> continuous range; if there is slightly less mental load on the programmer
> then they can use more of their brain for important details, to do more
> powerful things with the language, for example.

The difference between a functional language and assembly language is
mostly a difference in structure, not just syntax or semantics. But
many of the features of TADS and Inform are minor details of syntax, or
special built-in functions which could almost as easily be library
functions.

Not all, though. From my perspective, the greatest strength and the
greatest failing of the major IF languages are both unique to IF
languages.

Their greatest failing: Actually, I think TADS biggest weakness is that
too many "library functions" are actually part of the language, and
can't be changed. But my greatest surprise in reading the TADS and
Inform manuals was that both languages completely lack type-safety.
Yes, this is also an advantage, since it allows multi-typed variables.
But it's possible to have the best of both worlds by allowing _some_
variables to be "generic" and others to be type-specific, as Java does.
Of "real" programming languages, the only one I know with the same
problem is LISP.

Their greatest strength: Better object-oriented syntax. Yes, this is a
syntactic difference, but it's a *major* syntactic difference, not a
minor one. C++ requires class definitions to be split into a header
file component and a source file component. And C++ doesn't allow
specific objects to override class defaults. Instead, objects that have
unique behavior in one respect must have complete class definitions
written for them! In the end, this results in a lot of extra work.


As for other languages, I don't know any but Inform. Inform seems to
have mostly the same linguistic style as TADS, though the library is
more comprehensive. One difference is the built-in support for 64 bit
flags per object. But the biggest difference is that Inform has
built-in support for an object tree! This is its silliest feature as a
language. Because Inform has this built-in capability, less specific
code had to be written to permit things like containers or supporters.
But this built-in capability means that every little scrap of paper or
item of food in a game wastes some space remembering the fact that it
doesn't have any children in the object tree. And, even worse, the easy
implementation of containers and supporters in Inform makes it
impossible for an object to be both simultaneously a container and a
supporter--which would be a trivial task if the containers and
supporters and been defined using normal techniques.


I'm writing my own language, but I'm not making many innovations. My
system will have far fewer built-in functions and operators than TADS
and Inform, because I want the library to be more robust. But I am
adding one radically new feature, which I've never seen in any language,
and which I think can be very useful for interactive fiction: the
"branch" statement. For a really thorough discussion of the "branch"
statement, look back in this newsgroup a few months--I described it in
detail in January.

Basically, the branch statement is a combination and a logical extension
of the TADS concepts of a "Verify" routine and an Undo buffer. A sample
program:

int number = 0;
branch number = 1;
if (number == 1) ErrorMessage ("This is bad!", 1000);
else ErrorMessage ("Number == 0. This is okay.", 10);

This program will always print the message "This is okay" because it has
a lower error value than the message "This is bad." When this program
is run, the program will run normally until it reaches the branch. Then
it will memorize some information about the current state and "silently"
run the line "number = 1" and then finish the rest of the turn. Once
it's done, it will use the Undo buffer to return to where it was, and
see what happens if it skips the branched statement and then finishes
the turn. Whichever branch works better is then displayed.


What does this achieve? The same thing that TADS achieves with the
Verify methods--superb disambiguation. But with this technique, the
Verify methods don't have to be separate from the rest of the code, and
you can safely make changes to game state information since the Undo
buffer will restore the original state, and the game programmer can
explicitly use this technique to disambiguate in special situations.

I haven't played with this very much, but here's a simple example. I
created a simple parser for my authoring system that allows only
one-word names for objects. The parser matches the first object
possible, so it can't handle indistinguishable objects. It doesn't even
have any concept of scope--the sentence "Get knife" will be parsed as
though the object were present, and only after the parser has finished
with the sentence does another function complain that "You can't see the
knife here."

With such a primitive system, indistinguishable objects will cause
trouble. If a second knife is added to the game, the parser will still
always match the word "knife" with the original knife. So silly errors
can result. If you drop the first knife and in one room, go to the room
that holds the second knife, and type "Get knife" my system will reply
"You can't see the knife here."

Oops. My parser always matches the first object that works, and has no
concept of scope. How long do you think it would take me to implement
indistinguishable items? A half-hour? A couple days?

It took less than five minutes. All I had to do was change the generic
MatchWords function from:

return Word [Index] == Text or Synonym;

to:

if (Word [Index] != Text or Synonym) return 0;
branch return 1;
return 0;


Once I did that, the command "Get knife" was always interpreted in three
different ways (if two knives were in the game), and the best
interpretation was always taken. It's a weak example, but this command
is powerful enough to be useful for much more sophisticated
disambiguation tricks.


-Rúmil

Erik Max Francis

unread,
Mar 26, 1998, 3:00:00 AM3/26/98
to

Jeff Hatch wrote:

> Their greatest strength: Better object-oriented syntax. Yes, this is
> a
> syntactic difference, but it's a *major* syntactic difference, not a
> minor one. C++ requires class definitions to be split into a header
> file component and a source file component.

What do you mean "requires"? All that's necessary is that the class
declaration comes before the member function definitions. The most
natural way to do this with multifile projects is with headers and
sources files separated (headers contained macros and declarations only;
source files contain definitions, and perhaps some entirely local
declarations).

There is, however, no language requirement that the separation be made.

> And C++ doesn't allow
> specific objects to override class defaults. Instead, objects that
> have
> unique behavior in one respect must have complete class definitions
> written for them! In the end, this results in a lot of extra work.

This doesn't seem like such a bad idea to me. Making it explicit in the
declarations where there is nonstandard behavior looks like a good way
to write self-documenting code.

--
Erik Max Francis, &tSftDotIotE / mailto:m...@alcyone.com
Alcyone Systems / http://www.alcyone.com/max/
San Jose, California, United States / icbm:+37.20.07/-121.53.38
\
"I've got the fever for the / flavor of a cracker"
/ Ice Cube

Adam J. Thornton

unread,
Mar 26, 1998, 3:00:00 AM3/26/98
to

In article <351A05...@hatch.net>, Jeff Hatch <je...@hatch.net> wrote:
>Basically, the branch statement is a combination and a logical extension
>of the TADS concepts of a "Verify" routine and an Undo buffer. A sample
>program:
>
>int number = 0;
>branch number = 1;
>if (number == 1) ErrorMessage ("This is bad!", 1000);
>else ErrorMessage ("Number == 0. This is okay.", 10);
>
>This program will always print the message "This is okay" because it has
>a lower error value than the message "This is bad." When this program
>is run, the program will run normally until it reaches the branch. Then
>it will memorize some information about the current state and "silently"
>run the line "number = 1" and then finish the rest of the turn. Once
>it's done, it will use the Undo buffer to return to where it was, and
>see what happens if it skips the branched statement and then finishes
>the turn. Whichever branch works better is then displayed.

Interesting. Have you talked to Phil Goetz about his IF-in-Prolog
techniques? It sounds like you're doing a similar sort of thing to what
Prolog is good at.

How do you programatically determine which branch is "better"? "Better"
for what purposes?

>What does this achieve? The same thing that TADS achieves with the
>Verify methods--superb disambiguation. But with this technique, the
>Verify methods don't have to be separate from the rest of the code, and
>you can safely make changes to game state information since the Undo
>buffer will restore the original state, and the game programmer can
>explicitly use this technique to disambiguate in special situations.

Be careful with Undo, too; Grip shows (if you undo out of, e.g. the beach
in Fit II School) that TADS has some problems.

Adam
--
ad...@princeton.edu Cynical and drunk and boring someone in some dark cafe

Robert Masenten

unread,
Mar 26, 1998, 3:00:00 AM3/26/98
to

Jeff Hatch <je...@hatch.net> writes:


> I'm writing my own language, but I'm not making many innovations. My
> system will have far fewer built-in functions and operators than TADS
> and Inform, because I want the library to be more robust. But I am
> adding one radically new feature, which I've never seen in any language,
> and which I think can be very useful for interactive fiction: the
> "branch" statement. For a really thorough discussion of the "branch"
> statement, look back in this newsgroup a few months--I described it in
> detail in January.

[snip]


> This program will always print the message "This is okay" because it has
> a lower error value than the message "This is bad." When this program
> is run, the program will run normally until it reaches the branch. Then
> it will memorize some information about the current state and "silently"
> run the line "number = 1" and then finish the rest of the turn. Once
> it's done, it will use the Undo buffer to return to where it was, and
> see what happens if it skips the branched statement and then finishes
> the turn. Whichever branch works better is then displayed.

Believe it or not, AGiliTy/Magx actually uses a scheme roughly along
these lines for disambiguation, although it isn't as sophisticated or
general as what you're proposing. (And, of course, the details and
implementation end up very different, if only because AGT aspires to
be declarative rather than procedural.)

Robert Masenten

Jeff Hatch

unread,
Mar 27, 1998, 3:00:00 AM3/27/98
to

Adam J. Thornton wrote:
> How do you programatically determine which branch is "better"? "Better"
> for what purposes?

The "branch" statement works with a parallel "error" statement, which
ends a branch. The higher an error number is given to the end of a
branch, the worse that branch is. In general, the error messages that
indicate less understanding have the higher numbers. To test my system,
I just threw together a sequence of error numbers, in the following
order (from highest to lowest error numbers):

1. I don't understand your verb.
2. I don't understand your direct object.
3. I don't understand your preposition.
4. I don't understand your indirect object.
5. You can't see the [something] here.
6. You can't do that.
7. You don't have / already have the something.

(The numbers listed in this chart aren't the error numbers I
programmed. I gave the "don't understand" messages numbers in the
20,000s, and the "can't see" / "can't do" messages numbers in the
10,000s, IIRC.)

That sequence worked well enough for commands like "get knife" to work
quite well when two knives were in the game. If you have one, and the
other one is in another room, my sample program will run four different
possibilities, and choose message 7 over messages 1, 2, or 5.


-Rúmil

Jeff Hatch

unread,
Mar 27, 1998, 3:00:00 AM3/27/98
to

Erik Max Francis wrote:

>
> Jeff Hatch wrote:
>
> > Their greatest strength: Better object-oriented syntax. Yes, this is
> > a
> > syntactic difference, but it's a *major* syntactic difference, not a
> > minor one. C++ requires class definitions to be split into a header
> > file component and a source file component.
>
> What do you mean "requires"? All that's necessary is that the class
> declaration comes before the member function definitions. The most
> natural way to do this with multifile projects is with headers and
> sources files separated (headers contained macros and declarations only;
> source files contain definitions, and perhaps some entirely local
> declarations).
>
> There is, however, no language requirement that the separation be made.

I mean that in multifile projects it's practically impossible to combine
the two aspects of a class definition into one file. I've managed to do
that by using preprocessor directives, but then my debugger didn't
understand which files were source files and which weren't. The better
method is to put all member function definitions within the class
declaration, and allow the external files that need to refer to this
class to simply scan the class declaration and skip the member function
declarations. (Presumably C didn't use this technique because compiler
speed was far more of an issue back then.)

-Rúmil

John W Kennedy

unread,
Mar 27, 1998, 3:00:00 AM3/27/98
to

Andrew Plotkin wrote:
>
> John W Kennedy (jwk...@ibm.net) wrote:
> > JC wrote:
> > > I'm interested in knowing what things would/do make an IF language more
> > > suitable for Interactive Fiction.
>
> > I think the only important parts for the _language_ are:
> > a built-in lexer,
> > "soft" objects that either allow testing an unknown object to know
> > whether it has such-and-such a method or not, or else have have a benign
> > result when an unprovided method is invoked
> > convenient access to packed single-bit and single-byte object
> > attributes.
>
> I agree. Note that are language features, *not* virtual machine features.
> In particular, the lexer should not be hardwired in the VM (assuming
> you're using a VM model at all) and the object structure should be as
> little as possible, possibly not at all.

I don't know -- to some degree, it's hard to implement the "soft"
objects and the small attributes without cooperation between a VM and
the language, and, given a VM, unless a _lot_ of effort ala JavaSoft's
upcoming HotSpot is made, the lexer pretty much needs to be hard-wired
for acceptable performance (especially since most programmed lexers are
table-driven, making them in a way another level of VM). Perhaps the
ideal solution is to incorporate a lex-like function into the language,
with the run-time implementation "hard-wired" in the IF VM, providing
the best of both worlds.

I made some studies of providing Inform-like objects by direct use of
various object architectures: C++, Java and SOM (IBM's language-neutral
CORBA implementation), and they all entailed horrid amounts of overhead.
Existing object architectures for software component development do not
seem to mesh well with fuzzy simulation.

0 new messages