I6: Precedence of class property definitions not as described in DM4?

The DM4 p. 61 gives an example:

Object "goose that lays the golden eggs"
    class Bird Treasure;

and states: “(It inherits from Object first and then Bird and then Treasure, attribute settings and property values from later-mentioned classes overriding earlier ones, so if these classes should give contradictory instructions then Treasure gets the last word.)”

From the experiments I’ve been doing with Inform 6.31 (10th Feb 2006), this doesn’t seem to actually be the case. Instead, it looks like it’s the earlier-mentioned class (i.e. left-most in a class list) which gets precedence in defining either properties or methods, and the class used to start the object definition counts as the earliest of all. For example:

Class Bird
    with    wingspan 5,
            relevance 1;

Class Treasure
    with    score_value 10,
            relevance 100;

Object "goose that lays the golden eggs"
    class Bird Treasure;

[ Main x;

    objectloop (x ofclass Object)
        {
            print "The ", (name) x, " has a relevance of ", x.relevance, ".^";
        }

];

produces output (using frotz):

The goose that lays the golden eggs has a relevance of 1.
[Hit any key to exit.]

This shows that the relevance property’s value was inherited from the Bird class, not the Treasure class. Adding:

Bird "ostrich that lays the golden eggs"
    class Treasure;

Treasure "canary that lays the golden eggs"
    class Bird;

Object "crow that lays the golden eggs"
    class Treasure Bird;

changes the output to:

The goose that lays the golden eggs has a relevance of 1.
The ostrich that lays the golden eggs has a relevance of 1.
The canary that lays the golden eggs has a relevance of 100.
The crow that lays the golden eggs has a relevance of 100.
[Hit any key to exit.]

Note how the relevance score of 100 (from the Treasure class) is only applied to those objects where the Treasure class is “mentioned” first, either as the first word of the object definition or the first class of the class property.

Am I misunderstanding how the documentation says this is supposed to work? If not, is this an error in the DM4 or a bug in that version of I6?

4 Likes

My reading of DM4 is the same as yours. “Treasure gets the last word.” I’ll try compiling the example when I have some time.

You are right, the behavior of the compiler doesn’t match the manual. I tested it with the latest version of I6 (both Z-code and Glulx).

I’ll have to dig into the compiler code to see what’s going on. But if the compiler has been behaving consistently all this time, I suspect the right answer is to say that it’s a mistake in the manual.

(By the way, when I compiled your test, I had to add the line:

if (x.&relevance == 0) continue;

…to avoid a lot of runtime errors. Did you just not mention those?)

zarf,

I’m not sure why you might have gotten RTEs. Following is the complete code, just recompiled and still producing the output shown above.

Class Bird
    with    wingspan 5,
            relevance 1;

Class Treasure
    with    score_value 10,
            relevance 100;

Object "goose that lays the golden eggs"
    class Bird Treasure;

Bird "ostrich that lays the golden eggs"
    class Treasure;

Treasure "canary that lays the golden eggs"
    class Bird;

Object "crow that lays the golden eggs"
    class Treasure Bird;

[ Main x;

    objectloop (x ofclass Object)
        {
            print "The ", (name) x, " has a relevance of ", x.relevance, ".^";
        }

];

The code that you had to add was inside the objectloop? Would it make a difference that my sample code doesn’t include the normal files for parser, grammar and English?

Also, just for my understanding, does the snippet that you added make the same test as ~(x provides relevance)?

Oh, it’s because I was testing with the standard I6 library. So objects like the compass directions were defined (with no relevance property). Sorry about the confusion.

In this example, yes.

(The provides operator is a little smarter, e.g. for tests like "Foo" provides print_to_array. When called on object references, it’s the same.)

I just did a bunch of testing on this. I have written up a section for the I6 Addendum document:

The DM4 (§3.8) gives this example of multiple inheritance:

Object "goose that lays the golden eggs"
	class Bird Treasure;`

[This goose] inherits from Object first and then Bird and then Treasure, attribute settings and property values from later-mentioned classes overriding earlier ones, so if these classes should give contradictory instructions then Treasure gets the last word.

In fact this is not true (and never has been). The language behavior is somewhat complex.

Property values are determined by the first class listed, whether that is given as a directive (Treasure goose) or a class keyword (Object goose class Treasure). Of course, if the object itself gives a property value, that overrides all inherited values.

If one class inherits from another, the derived class property overrides the superclass property, as one would expect.

Additive properties follow the same logic, but their values accumulate, rather than overriding each other. An additive property gains values from the object’s own definition (first), then from each class in (forward) listing order.

[[This supports the I6 library convention that additive properties (before, after, life) contain lists of inline routines. These are tested in forward order; each routine may return true to indicate that the event is handled, or false to pass control to the next routine. The precedence order is thus, again, the object followed by each class in listing order.]]

Attributes follow the same rule, except that classes cannot negate attributes set by other classes.

Class Bird has ~heavy;
Class Treasure has heavy;
Object goose class Bird Treasure;

In this example, the goose object gains the heavy attribute because at least one of its classes sets it. This will be true no matter what order the classes are listed in. The has ~heavy declaration in class Bird has no effect.

Objects can of course use has ~attr to negate attributes inherited from classes. Derived classes can negate attributes inherited from superclasses. The limitation applies only to conflicts between an object’s classes.

[[This is arguably a compiler bug, but it has been established behavior since Inform 6.0, so it may not be practical to change it.]]

3 Likes