
Title: Lessons Learned


Group: Inform: Gotchas

Using _self_:
Don't use _property_ rather than _self.property_ within a method!  They are not identical.  The compiler warns sometimes when assigning *to* the property, but not when assigning *from* the property.  (It seems like the first form refers to the location of the property itself, while _self.property_ actually dereferences it?)

Property and local variable name conflicts:
Don't even have a local variable with the same name as any property you might refer to in that method.  For example, which writing DM's _constructDeed(verb)_ method, I named the parameter _verb_.  However, then the line
>
>  deed.verb = verb;
>
(where _deed_ is an MEvent with a _verb_ property) would lead to a strange crash (unable to access object).  This is likely the same problem as above--it seems that properties are all in the global name space in some strange way.

Property and global variable name conflicts:
(Yes, there is a theme here.)  You can't have a property with same name as a global variable.  This means, for instance, that no class can have a property with the same name as any global in the Inform library .  Doing so results in a compile-time error when compiled with the library.


_create()_ and _destroy()_ methods:
A class object _provides_ the creation routines: _create()_, _destroy()_, etc.  By default, an instance of that class does not.  However, if you actually try to print the location of these class routines, you'll get a crash: they don't actually exist anywhere (read only, I guess?).  Interestingly, if you "override" these by defining them in your particular class, your instances will now also _provides_ them.  (This makes sense to me).  However, now you can print the location of the class's routine--it's the same as the instance's.  Yet, despite the same memory location, the two are not identical: _myList.destroy()_ and _List.destroy(myList)_ both run the destroy() method overridden by the List class definition, but only the latter will actually destroy the myList object.  (Huh.)  Yet, you can't call the latter form from within the destroy() method's definition--it's an infinite recursion.  (I'm assuming the latter form calls the former?)  It seems the safest way to think about it is that an (invisible) form of create/destroy associated with the class actually creates/destroys the object itself and afterwards/before doing so calls an embedded routine of the same name in the object itself.

Also, although you pass them a parameter when you call these methods, you don't use a parameter when you define them.  That is, use _self._ if you want to set or free instance variables.


_create()_ method:
Only takes a max of 3 parameters.

Declare instance variables before use:
You need to declare instance variables before the methods that access them.  For example, you can't set the value of an instance variable in your _create()_ constructor if the variable is defined after the _create()_ definition in the class.  What's surprising here is that the problem doesn't show up at compile-time, but at run-time (which still doesn't quite make sense to me).

These bugs can be very subtle!  For instance, a private method that accesses a public instance variable.  If you declare the method first, then the variable, it compiles and runs, but the value of the variable is always 0 while in the method.  (In my experience.  Again, why?  Don't know.)  Remember you can interleave multiple _with_ and _private_ sections though, so you can still safely declare public instance variables before private methods that way.

_DEBUG_ mode requires standard library:
When debugging with the switches that turn on DEBUG mode when compiling my own code, I get this:

> <veneer routine 'CA__Pr'>(1): Error:  '=' applied to undeclared variable
> > ebug_flag=debug_flag-n;
> <veneer routine 'CA__Pr'>(1): Error:  Expected assignment or statement but found <constant>
> > ebug_flag=debug_flag-n;
> <veneer routine 'CA__Pr'>(1): Error:  '=' applied to undeclared variable
> > ebug_flag = debug_flag + n;
> <veneer routine 'CA__Pr'>(1): Error:  Expected assignment or statement but found <constant>
> > ebug_flag = debug_flag + n;
> Compiled with 4 errors (no output)

Same happens if I define my own DEBUG constant.  This is with the 6.30 and the 6.31 compiler (I upgraded because of this.)  However, including the standard libraries fixes the problem.

Logic operator precedence:
Inform logic operators (&&, ||, and ~~) all have the same precedence, rather than the normal ~~ before && before ||.  && and || should work left to right, but apparently ~~ doesn't have too, since ~~A && B was being evaluated as ~~(A && B) rather than (~~A) && B.



Group: Inform: Clarifications

String comparison and _switch_:
The DM4 just says cases need to be constants, which includes objects.  It does not, however, include strings literals (won't compile).  This actually makes sense, since Inform doesn't support string comparison or manipulation.  Variables are just references, even to strings, and so comparing two different references is generally meaningless.  However, you can define an actual _Constant_ to hold the string and do things that way.

Inheritance and the max number of instances:
As the Inform DM4 says, you have to specify the total number of instances of a class that you will ever create.  However, you don't need to do the same with any parent classes.  Example:
>
> Class Parent;
>
> Class Child(50)
>   class Parent;
>
> [Main
>   obj;
>   obj = Child.create();
> ];
>
This will work (even when the classes actually define some properties that take up space)

_copy()_ method:
Will also copy over values specified in the superclass, even if they're private.

Object names and printing:
Remember that to print an object, use the _(name)_ printing rule.  What is printed by this is either the object's (variable name) or the string associated with it at definition:
>
> Object obj1;
> Object obj2 "My second object";
> [Main
>   print (name) obj1, "^";  !prints: (obj1)
>   print (name) obj2, "^";  !prints: My second object
> ];
>
However, this associated string does not seem to be either the _name_ property, or the _short_name_ property. This makes some sense, since this behavior is part of the core language, while the other two properties are defined by the Inform library.  The value of this string cannot be changed at runtime (at least not without knowing how to access it!). 

Room _name_:
The words listed under a room's _name_ property are things the player does *not* need to refer to (rather than ways to refer to the room itself).  Listing things here simple gives a polite "yeah, I know what you're talking about but you don't need to refer to it in this game" message.

LibraryMessages:
Don't think of this as a step along the way of verb processing.  Instead, it's more like a side-step: whenever the system is ready to print a message, it consults LibraryMessages on what to print.  Everything that's going to happen or not (by the default rules) has already happened at this point.  And this is usually called after the _AfterRoutines_ (if any), so, in that sense, it's not happening during the During part of action processing.

_AfterRoutines_:
If a specific after routine (such as in the room) returns true, all other subsequent post routines (such as after for the object and then PostGameRoutine) never get called.  

Probably due to this, _Examine_ doesn't act like an Group 2 verb--no after routines get called--if there is no description defined for the examined object.  (I'm assuming the library routine that provides the default "You see nothing special" message returns true or simply fails to call the after routines in this case.)  A few other Group 2 verbs do this too on a failure.  For instance, trying to _Take_ a static object fails properly but the after routines never gets called.  This all makes sense if you consider that AfterRoutines only get called if the verb actually occurred/completed successfully.

_compass_:
The compass object apparently gets refreshed each turn to contain only those directions in which you can currently move.  So don't try using a direction if it's not currently in the compass!

_found_in_: 
found_in only works with rooms.  So if you try to put an object (such as the sky) in a container (such as a porthole) it will actually be placed in the room containing that object.

_random()_:
Inform's DM4 says you can seed the generator by giving it a negative number, as in _random(-14)_.  This doesn't work under Glulx though; you'll get a negative random number.  Instead, to seed the generator, use _@setrandom 14_ (or some other seed value).  

Note that this does not give consistent performance across interpreters, however.  For example, while Gargoyle's Glulx might give the same sequence each time the game is played, a different sequence is given if the same game is played in Zag.  Also, it appears that changing the code base in Gargoyle Glulx will also change the random sequence generated (despite still having the same seed).


Group: Future Practices

Choice of language:
For a new, large project, choose a language 1) that you like, 2) that you know well, and 3) that is suited to project.  It's better to learn the intricate details of a language with a couple smaller projects than a large one.

I like Inform, but the quirkiness and the constant little bugs because I don't know it well are frustrating and time-consuming.  Also, in retrospect, it may not have been all that well-suited to the project.  

