Okay, I'll try again.
By "compile-time" I meant during the process of parsing and lexing the source code into something that could be considered executable, be it some sort of byte-code or real machine code. A lot of what are often considered scripting languages do this every time they run a program - Perl, for instance. However, in the following description I would prefer you think in the context of a C++ compiler as it has a more conventional compile cycle.
This process normally populates a number of symbol tables for various items: variables, functions, classes and objects. The intermediate representation of the code relies on these symbol tables to know what the data it points to is for, contains and is allowed to do. Thus, our first dictionary.
The second dictionary mapping takes place when we examine those symbol tables a little more closely. Classes with methods would effectively get their own symbol table* for their calls. Calls to those class methods would get replaced with a compact representation to that symbol table entry. We now have our second dictionary.
Now some of this information needs to stick around after the parsing and lexing is complete. Why is that? Well, the first answer is idiotically simple: external functions need to be link-edited by name. :-) Slightly more advanced we might have functions of our own that need to be made visible - again by name - to other compiled modules. A third dictionary, though not one pertaining directly to OO.
However, there is some remaining symbol table information required for runtime behaviour. And that is for a technique called dynamic dispatch. From a dictionary point of view, dynamic dispatch says that if a function call can't find its function in the dictionary attached to it's object, it can look in the dictionary attached to its parent object. This information is taken from our second dictionary type to create a fourth.
At this point we come to the point I was originally making. All of these dictionaries are intended for performing a fairly specific function of mapping names to information. They generally function fairly invisibly and scale up and down usually more than enough to suit their specific purpose. At no point are they general purpose dictionaries. Instead, the fact they are really dictionaries - and, in fact, that there are quite a number of them - is abstracted beneath the slightly more specific role they perform. Like symbol table lookup. Or dynamic dispatch.
Thinking of polymorphism, inheritance, dynamic dispatch and other aspects of object-oriented or object-aware programming as dictionary-based logic is an interesting academic exercise, but IMHO reducing the former to the latter is a distinct step backwards.
Wade.
* Whether or not this really happens is actually an implementation detail and is unimportant to this discussion.
Appendix.
I should re-iterate that I'm a fan of clever decision trees in my logic. Handing a function pointer to a piece of code to change its behaviour I find preferable to coding in a huge switch() statement. Code that can do wildly different yet oddly similar tasks based on what its initial data is appeals strongly to me. Taking advantage of object inheritance and programmable code in the parent code is one such powerful tool. It makes my code smaller and smarter.
Edit: Corrected a noun's state in the footnote to reflect what I originally meant to type. Bryce didn't even notice...
"Ah. One of the difficult questions."
Edited by
static
Dec. 26, 2002, 03:42:13 AM EST