A Taste of Nested Classes, part 1

In the recent posts I mentioned that because of class nesting the receiver of an implicit-receiver message send is not necessarily “self”. The full story with a comparison to other approaches is told in Gilad’s paper On the Interaction of Method Lookup and Scope with Inheritance and Nesting. In this series of posts I provide an informal example-oriented introduction for someone familiar with Smalltalk, to help better understand the full story.

First an important terminology note. When we say “class” we can refer to either a (textual) class definition or a class metaobject. Because the relationship between the two in Smalltalk is normally one-to-one, we can pretty much ignore that ambiguity. The Newspeak situation is different, and not thinking of definitions and metaobjects as separate things is perhaps the easiest way to get confused. To avoid that, in this post I’ll stick to spelling out “class definition” or “class metaobject” fully, unless there is no danger of confusion.

Quite naturally, “nested classes” first of all mean nested class definitions. Why would we want to nest one class definition inside another? This, of course, largely depends on what exactly such nesting translates to and what benefits it brings, and that’s what this whole series is about. For starters (even though this by far is not the primary benefit) let’s assume that we do it as a matter of code organization. For example, suppose we are implementing the immortal Asteroids game and decide to nest the definition of the Asteroid class inside the Game class. In the current Newspeak syntax it would look something like this:

class Game = ()
(
    class Asteroid = () ()
)

This rather basic implementation will not bring us hours of gaming enjoyment, but at least we do now have one class definition nested inside the other. How do we work with this thing?

Starting from the outside, in the current Newspeak system hosted in Squeak, this definition would create a binding named “Game” in the Smalltalk system dictionary, holding onto the Game class metaobject. This means that in a regular Smalltalk workspace we could evaluate

Game new

and get an instance of Game.

I want to emphasize right away that despite what this example may suggest, Newspeak has no global namespace for top-level classes. This availability of Game as a global variable in Smalltalk alongside the regular Smalltalk classes is the scaffolding, useful here to write examples in plain Smalltalk to keep things more familiar for the time being.

So what about Asteroid—how could we instantiate that one? Nesting a class in another essentially adds a slot to the outer class definition, with an accessor to retrieve the value of the slot. That value is the metaobject of the inner class. So, we get an inner class (metaobject) by sending a message to an instance of its outer class (metaobject). Notice that it’s not “by sending a message to the outer class”—”an instance of” is an important difference:

game := Game new.
asteroidClass := game Asteroid.
asteroid1 := asteroidClass new.
asteroid2 := asteroidClass new

So far nothing really surprising, apart from having to go through an instance of one class to get at another class. Let’s now change the example:

class1 := Game new Asteroid.
class2 := Game new Asteroid.
asteroid1 := class1 new.
asteroid2 := class2 new

This is more interesting. asteroid1 and asteroid2 are both instances of Asteroid and behave the way the class definition says. But since the Asteroid class references come from different game instances, what can we expect beyond that? For example, what would these evaluate to:

asteroid1 class == asteroid2 class
asteroid1 isKindOf: class2

The answer is false, for both of them. The two instances of Game produced by Game new evaluated twice contain independent Asteroid class metaobjects. Their instances asteroid1 and asteroid2 are technically instances of different classes, even if those metaobjects represent the same class definition. (Relying on explicit class membership tests is a poor practice in Smalltalk, and this illustrates why it’s even more so in Newspeak).

Let’s now look at another variation:

game := Game new.
asteroid1 := game Asteroid new.
asteroid2 := game Asteroid new.

In this case

asteroid1 class == asteroid2 class

is true because the two sends of the message Asteroid retrieve the same Asteroid class metaobject held onto by the instance of Game.

Let’s now summarize the objects involved and their relationships. They are shown in the following picture

Nested classes illustration

There is a class metaobject for the Game class, contained by the system dictionary (shown in gray to emphasize that it’s not an “official” part of the scheme). An instance, connected to the class with a dotted line to represent the instance-of/class-of relationship, holds the Asteroid class metaobject. That metaobject has instances of its own, which are the asteroid1 and asteroid2 of our example.

If the Game class has additional instances (one such instance is shown small in the diagram), each instance has its own copy of the Asteroid class metaobject. If there were multiple levels of nested classes, the scheme would repeat for the deeper levels.

The diagram also illustrates a concept that will be central to the next part of this series: the enclosing object. In terms of our example, an enclosing object of an asteroid (instance) is the game (instance) that owns the class (metaobject) of the asteroid. This relationship is obviously quite important and useful: simply put, it means that an asteroid knows what game it is a part of. That’s simply by virtue of nesting the Asteroid class definition inside Game, without having to explicitly set up and maintain a back pointer from an asteroid to its game.

(Continued in part 2).

13 Responses to “A Taste of Nested Classes, part 1”

  1. Great article: I can’t wait to read the next one :).

  2. Bob says:

    Love it. Please keep going. And I fervently hope for a funding solution.

  3. Paul Beckford says:

    I love the idea, but I’ve got a question:

    OK. So if I explicitly specify self as the receiver then the method lookup will traverse the inheritance hierarchy as usual. If not it will look in my class first then proceed up the lexical scope.

    So what if I want to explicitly lookup a method in the containing lexical scope. Is this possible? For example if Asteriod had a game method too, how could I unambiguously lookup the game method in Game? Do I get a reference to the containing lexical scope for free?

    Cheers,

    Paul.

    Cheers Paul.

  4. Vassili Bykov says:

    Yes, or sort of, depending on what’s your intent: lookup in containing lexical scope only, or unambiguous lookup in Game. To me the two mean slightly different things.

    The feature I haven’t mentioned so far are outer sends. The syntax “outer <class-name>” fetches a reference to an enclosing instance of <class-name>. So, you could unambiguously invoke the game method in Game bypassing one in Asteroid by writing “outer Game game”. However, this isn’t quite what I would call lookup in the containing lexical scope. That would mean ignoring the receiver class and looking for a method in any of the containing classes. Here, we are fetching a reference to the game instance and then explicitly sending a message to it. No lexical lookup of the game message happens at all because it’s not an implicit send.

  5. Paul Beckford says:

    Hi Vassili,

    I guess I didn’t make my self very clear but you did answer my question. The “outer ” is what I had in mind. I agree this isn’t the same as a “lookup in the containing lexical scope”, but it is an explicit reference to a specific lexical scope. I think I confused the two things in my question.

    Thanks Paul.

  6. Paul Beckford says:

    Hi Vassili,

    My last response isn’t quite right either is it? I’ve re-read what you’ve said and I think I get it now. A bit difficult to explain, so I’ll leave it to you in the next installment :)

    Cheers,

    Paul.

  7. The correct link for the “dyla07-Gilad.pdf” paper is http://dyla2007.unibe.ch/?download=dyla07-Gilad.pdf.

  8. Forget the previous comment and this one, it was a firefox browser problem.

  9. Gaboto says:

    Hi Vassili Bykov, I always read your posts I really like it and newspeak. Congratulations!
    Can I ask you something that has nothing to do with it? What program do you use to make the UML drawings? they are really beautiful.
    (you can answer it to my mail if you prefer)
    Bye!
    Gabriel

  10. Vassili Bykov says:

    The program is Adobe Illustrator. However, keep in mind that it’s a general-purpose drawing application, so I don’t recommend it as a diagramming tool for everyone.

  11. Marty says:

    I understand that the 2 Asteroid metaobjects can be different. I’m not clear about WHY they need to be different, at least in this example. Couldn’t the Asteroid method on Game arrange to always return the same meta-object?

    Thanks.

  12. The correct link to the “On the Interaction of Method Lookup and Scope with Inheritance and Nesting” article is http://bracha.org/dyla.pdf.

  13. Vassili Bykov says:

    Thank you, Damien! I fixed it in the post too.

Leave a Reply

For spam filtering purposes, please copy the number 1422 to the field below: