Language theory: classes within classes

| | Comments (2)

So now that REALbasic has the ability to declare types within classes, it makes sense to ponder the idea of allowing one of the more pervasive types to be declared within a class: another class. Let's take a simple example of what sort of problems this would solve.

You have a project you're working on where you control a robotic arm via a serial communication link. Since you adhere to OOP as strictly as you can, you want everything to be as encapsulated and clean as possible. Part of the serial protocol is that you need to send a keep-alive message across the wire every minute, otherwise the robotic arm will turn against its masters and find Sarah Conner. You have a class for the serial communications, and you have a timer to send the keep-alive packet -- the most logical place for that timer is inside of the serial class, as a private inner class. This is the best design because nothing other than the serial class actually requires use of the timer, so it should be hidden away as much as possible.

Now that you've got a concrete example of when you might want to declare a class inside another class, let's talk about some of the ramifications of this. For starters, this has a ton of strange edge cases to it that other type declarations do not suffer from. Structures, enumerations and delegates do not have concepts like inheritance, but classes do. So the question becomes: how should inheritance work? What should the rules be? Can an inner class inherit from an outer class? Or could an outer class inherit from an inner class?

These edge cases prove to have some highly entertaining situations. Imagine this little twister of a scenario:


Class Outer
Inherits Inner
Class Mid
Inherits Outer

Class Inner
End Class // Inner

End Class // Mid
End Class // Outer


Uh... which end of the dog is the tail wagging from, exactly? The inner class controls the behavior of the outer class, which in turn controls the behavior of the mid class. That's quite a mess to get into, even though there's no inheritance recursion per se.

What about this example?


Class RootLevel
Inherits OtherRootLevel
End Class

Class OtherRootLevel
Class Inner
Inherits RootLevel
End Class
End Class


In this case, RootLevel depends on OtherRootLevel, but OtherRootLevel depends on Inner, which depends on RootLevel. So again, it's not a direct recursion in terms of the inerhits statements, but it's still a total mess.

Thankfully, this mess has two relatively easy rules to live by, with a very simple over-arching explanation. When defining a class' inheritance chain, there can be no references to it's containing outer class, if any. Also, when defining a class' inheritance chain, there can be no references to any inner items it contains, if any. The explanation for why this is necessary is: a class isn't fully defined until all of its parts are defined, so there can be no loops in class definitions. This is already true to a certain extent by the fact that you're not allowed to have recursive class inheritance.

So how would the search structure work for this sort of feature? Do we look in the container, or in the superclass, if the type cannot be located? Well, I imagine this working the same way it works currently: look up the container chain, then at each container, look up the superclass chain. So imagine this situation:


Class Foo
Protected mFoo as Integer
End Class

Class Bar
Inherits Foo
Class Baz
Sub Wahoo()
// Not legal, because a Baz is not a Bar, so it doesn't
// inherit instance members!
mFoo = 12

// Is legal, because Baz has access to all of Bar's private
// and protected members, and Bar inherits from Foo which
// has this protected property.
dim f as new Bar
f.mFoo = 12
End Sub
End Class
End Class


The search pattern for mFoo is to look first at Baz. Nope, it doesn't have it. And Baz has no superclass, so we move up the containment chain to Bar. Bar doesn't have it, so we check the superclass and come to Foo. Yup, Foo has it and so we can at least *find* it. Now we can check the access mechanism for it to see if the scope allows access.

Another example of this would be for type resolution.


Class Foo
Class Bar
Class Baz
Sub Wahoo()
dim f as new Foo
End Sub
End Class
End Class
End Class

In this case, the search hierarchy is much the same as our previous one -- just walk up the containment chain until we find Foo, and Baz.Wahoo does have access to Foo. This is how it already works with enumerations, structures and delegates declared in classes. So it makes sense for classes to behave the same way.

So whaddya think? What sort of edge cases can you think of that I've not already covered? I'm just pondering the various behaviors that will need to be defined for this sort of a feature, and I'm certainly not promising that the feature will be implemented this way, or implemented at all. ;-)

Oh, and before anyone suggests it: no, I will not be modeling this feature after Java. Under no circumstances will there be instance vs shared inner classes, because that's just plain ugly. All classes are shared -- which makes them behave in a strikingly similar fashion to classes within a module.

2 Comments

The first thing I think is that using an inner class to provide a Timer is the wrong way to go :) I'd use a Timer whose Action event can be provided via a delegate, thus freeing you up to think about implementing nested methods instead of inner classes.

@Charles -- lol, you're right, in this case, allowing users to say "this method is the Timer's Action event" via delegates would certainly be a cleaner approach. Sorry my trivial example wasn't perfect. ;-)

Leave a comment