6 ms·
I think there's some confusion about the analogy being drawn here. Biological classification is, like you said, a question of shared implementations. That's use
by icambron 13y ago
I think there's some confusion about the analogy being drawn here. Biological classification is, like you said, a question of shared implementations. That's useful and it works especially well in biology because organisms are largely built through specialization; i.e. it really is a tree, where ancestry groups organisms by shared genetic material.
But to begin with, we're not classifying things; we're building them. Software is designed; you don't (or at least shouldn't), construct your programming abstractions by progressively mutating the different parts of your code. Nature had to reimplement flying for the bat, but you don't have to! You get to build a bat by strapping wings onto a rat, and you know how to build wings because you already did it for birds. But the hierarchy system doesn't let you do that; you'll have to do what nature did and completely reimplement flying for the Rodent subclass of Mammal, since Bird is nowhere near it in your hierarchy diagram. That's the essence of the author's point, as I read it.
I suspect Raganwald would probably go a lot further than an OOP+mixin approach (and there is no shortage of good alternatives), but the simplest way around this kind of thing is something like this piece of Ruby:
class Bat
include Flying
include Rodent
include Echolocation
end
You get the idea, and it's pretty easy to port to JS. It also lets you build the griffin and the centaur, because the software doesn't have to follow nature's rules about how to derive one thing for another. Constraining your code's structure to evolution's is-a model is a straight jacket.
So that's implementation, but what about interface? We actually do care about outward behavior, and making our interfaces reflect that is important. Morphological classification may not tell you much about how a bat works inside and what other species a bat is evolutionarily close to, but it remains the case that if you want to know what a bat does, flying is a big part of that. What if I want to know whether a given animal can get over my fence? How do I ask if a creature can fly? I don't care at all that bats are closely related to mice because they're acting nothing like mice when zooming over my fence. Do I really add canFly() to some Animal superclass and have flying creatures override it? How many features will I detect that way? But with our mixins, we can just ask if it includes Flying, because the structure of our code captures that. Codifying the structure of your interface into the structure of your classes is part of the point of OOP, but it turns out that interfaces don't fit a simple inheritance mold. So you need more powerful tools to describe that structure.
This issue comes up enough in gaming that the no-hierarchy-lots-of-mixins approach has a name: the entity-component model. I bring this up not because I think the point applies differently to games but because it's a domain where you might actually build a bat.
Finally, how far would we really be willing to take biology's classification-by-implementation philosophy, even if classifying pieces of code were really our goal? We could end up with superclasses like UsesAStar and HashSet. Not really the point of all of this, right?
Long story short: I agree with the article and think the biology doesn't contradict it, but think we should probably stop using evolution as a conceptual model for how we organize our code.
- seanmcdirmid 13y ago> Software is designed; you don't (or at least shouldn't), construct your programming abstractions by progressively mutating the different parts of your code. Is this another ad for big design upfront, aka waterfall? It is a nice ideal, but when you get down into it, software really is evolved since requirements are constantly changing and your understanding becomes better. > You get to build a bat by strapping wings onto a rat, and you know how to build wings because you already did it for birds. Really you don't. The bat flies quite differently from the bird, with substantially different attributes. Strapping bird wings onto a rat often isn't going to work. > This issue comes up enough in gaming that the no-hierarchy-lots-of-mixins approach has a name: the entity-component model. This is actually much older than that (Common Lisp Flavors), and languages like Scala include mixin-like traits, not to mention the work of Bracha and Cook. Inheritance is still heavily involved in that though, it is not "mixins or inheritance" but "inheritance with mixins," it still very much is OOP, and you are still defining a DAG if not tree. > Finally, how far would we really be willing to take biology's classification-by-implementation philosophy, even if classifying pieces of code were really our goal? We could end up with superclasses like UsesAStar and HashSet. Not really the point of all of this, right? Why not? Why not classify all of our code at the finest grain we can? Of course, existing languages are poorly suited to that task, but we can always define new languages where this isn't a PITA. Or we could just use machine learning to figure out what the connections are automatically (this will happen eventually, just not for another 10 or 20 years I hope).
- icambron 13y ago> Is this another ad for big design upfront, aka waterfall? Not at all. You won't even know what the right abstractions are upfront. If you want to express the point here in terms of coding process, it's something like "refactor your code into this". That the design emerges iteratively isn't the same thing as saying there's no design. Contrast to evolution, which is not just iterative but also has no design at all. No one gets to sit around and think, "huh, let's reorganize this". Because if they did, maybe organisms in convergent evolution would share code. Edit, let me try that point again, because I don't think that was very clear. You may design iteratively, but you're still designing. "Oh, these things have x in common, so let me build an abstraction that captures that commonality and have them use it." But evolution can't do that. The only design tool it has is mutating existing code into additional subclasses. You have more tools than that, so as a metaphor evolution's "designs" can be described by simpler ontologies. So why limit yourself to those ontologies? You can think of evolution as having ridiculous limitations on the kind of design work it's capable of, which are independent of the fact that it's iterative. > Strapping bird wings onto a rat often isn't going to work. That you have to implement genuinely different things differently is true enough, but not really challenging the point that class hierarchies make encapsulation harder. Also: maybe an engineer would design a bat a bit differently inside so that it could use the avian wing tech? Just a thought. > Why not classify all of our code at the finest grain we can? My point there was that if you had choose one axis on which to classify parts of your code, implementation details wouldn't likely be it. That you have to choose is a consequence of the strict single-inheritance model here. Completely classifying a software abstraction is identifying the point n-dimensional space that describes it in terms of all the different ways to categorize it. A fool's errand in general, I suspect, but at any rate not possible under the class inheritance model (single or multiple). Edits: multiple for clarity