Thursday, October 12, 2006
This is a followup to a series of previous posts; the previous one was The Zen of Python - Part II and the introduction is at The Zen of Python - Part I. If you are lazy to read that introduction, this is about some Python design principles analyzed from an Eiffel perspective and comparing it with other languages (specially Python itself).
Simple is better than complex.
Simplicity is one of the most shining aspects of Eiffel. It lacks several features present in other languages, providing instead a few consistent but powerful ones that solve several problems together. For example, the Eiffel "class" concept serves also as a "module" concept (present in other languages as a separate thing). There are no functions and methods, everything is a class feature. Access routines and attributes are slightly different concepts in some cases, but for most purposes they are just "queries". Eiffel inheritance serves the purpose that other languages solve with inheritance, module importing, interfaces, mixins and a plethora of other constructs.
The nice thing is that most of this concepts are powerful but still follow simple and easy to understand rules. They are not swiss-army chainsaws, they are tools that simple adapt to several tasks, making the user unaware that they are actually different tasks.
One of the examples of this is the way that multiple inheritance (MI) works in Eiffel. The possibility of using MI is one of the things that make Eiffel inheritance so powerful. However, among other languages with MI, Eiffel has some of the simplest rules available, thanks to the properties of "one name, one feature", symmetrical inheritance (the parents of a class have no "order") and explicit conflict solving. Compare this with languages which use the C3 method resolution order; C3 has nice mathematical properties, but gets a little hard to explain. So most developers of these languages (I have informally polled several python developers), don't know
actually how their MI works. It is hard to understand intuitively what will happen in a class hierararchy with heavy use of MI and repeated inheritance; the Eiffel library has a lot of that and still method resolution is simple (I would dare saying obvious) to understand.
Complex is better than complicated
This is one of my favourite points in "The Zen of Python". Although "simple is better than complex" is easy to defend, there is always a limit about how much simplification you can add to a language without forcing the developer to workaround the "simple" mechanism, creating code that is actually more complex than the complexity that would have been added by a new language mechanism. As Albert Einstein used to say "Make everything as simple as possible, but not simpler".
I find an example of this in the ability of having multiple constructors in Eiffel. It sure is "simpler" to do as most other popular languages (C++, Java, Python) and offer a single constructor in each class; but when there is the need to initialize an instance of a class in several different ways, the "simple" solution turns out to be more complicated: it demands the programmer choosing different types to do syntactic overloading, or adding "flags" to the constructor, or using arguments with default values and have a complex logic with if's inside the constructor trying to guess what kind of construction is needed (the last two options get worse when trying to inherit constructors). Eiffel offers the possibility of having more than one creation routine, which is a bit more complex than other "simple" languages, but actually removes complexity from the software construction process.
Eiffel's multiple inheritance is mostly easy-to-understand, but it too has its dark corners: some pathological cases involving "select", for example. I'm disappointed that ECMA Eiffel didn't kill off "select". Sure, that would have meant having to use client-supplier instead of inheritance in a few complex situations, but (as the Gang of Four argue in "Design Patterns") that's not always a bad thing.