I have most of the work fleshed out for the world, things and rooms. However, I've found that I must have a lexicon representing parts of speech. The problem arose when I was considering how to describe paths from room to room. I don't want to force people to only be able to use north, south, east, west, up and down. If someone wants to add "northeast", who am I to tell them no? Arbitrary constraints are annoying. The problem arises if someone adds the direction "northaest". They probably didn't mean that. By requiring the programmer to specify a lexicon, we can check that the word exists in the lexicon and throw an exception if they try to add something for which no word exists.
Each word can optionally have synonyms and no synonym may be duplicated for a given part of speech. However, words may be duplicated if they are different parts of speech. For example, if I have the word "carry", "bear" might be an acceptable synonym. However, "bear" can also be a creature you might encounter. Fortunately, well-designed grammars can allow for such potential ambiguity and I think I'll shoot for that.
One benefit of this is that I can allow programmers the ability to write words in any language. When the grammar is added, he or she will be able to provide a grammar for a given language and have a natural language interface in the chosen language.
One tiny drawback is how I currently have all messages hardcoded in one language (English). Since all exceptions are being funneled through a _croak method, I have one convenient place to allow localizations (a little trick I've learned from Bricolage) and this will make this feature easier to add when I need it. Perhaps I'll create a Pig Latin interface
One minor annoyance I've found is how the Room is a special type of Thing. There are a couple of cases where a Thing has to handle things differently for Rooms but overriding the method in Room doesn't work because of the order of messages (if a Thing receives a message and a Room is part of that message, there's some odd behavior there).
For example, if I want to put something in something else, I have to make sure that both of those things are in the same location. I can't put a ring inside of a box if the ring is in a different room:
That fails if:
$container->location ne $containee->location;
However, the location of the room is the World, not the room itself. Rather than have a thing have any knowledge of subclasses, I've found a more general rule:
$container->location ne World->get_world
&& ( $container->location ne $containee->location )
In short, if something is contained in the world itself, it's an all purpose container which can contain anything else. I don't have to put special knowledge of rooms in the Thing class. There have been a couple of times where it would be helpful if Things knew about Rooms (the other one has been a weird constructor problem) but I've always found that the framework has allowed me to find a general purpose rule around it. However, I do wonder if I've set up my classes wrong. Sometimes I would be simpler if a Thing was a subclass of Room, but a Room truly is a special type of Thing and the Room class has some special properties which Things should never have (paths to other rooms, for example).