Tuesday, July 04, 2006
This is a followup to a previous post titled 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).
Beautiful is better than ugly
I will not write much about this one. It is about a very subjective idea (beauty) and the assertion is mostly tautological in nature: who would say that ugliness is better? Eiffel users think that Eiffel is beautiful, but most advocates of any language think that of their language.
Anyway, even when almost anyone would agree with "beautiful is better than ugly", a lot of language communities consider beauty as a secondary feature, and one of the first things being traded for other valued attributes (efficiency, compactness, sheer number of features).
Eiffel and Python were designed to be beautiful. At least beauty in the sense of elegant (simple problems should have simple solutions), simple (a term that in engineering, and software engineering in particular, is almost a synonym of beautiful), readable. Most of these will be covered later in this article or in later ones.
Explicit is better than implicit
This rule describes Eiffel more faithfully than Python. After all, static typing, Design by Contract are language features which provide a way to describe more explicitly the semantic interface of routines. Which one do you find more explicit,
prefix (size: INTEGER): STRING is
size >= 0
Result /= Void
Result.count = size.min (count)
Other examples of explicitness are having to indicate when redefining a feature (which could be done automatically, but could let slip some obscure bugs), or when a contract is being redefined (the "require else" and "ensure then" clauses). In this aspect perhaps Eiffel is more pythonic than Python.
Python is very clean in the sense that, despite not forcing too much explicitness, it requires a minimum of it in every place. I can not imagine now a Python example where an operation is done without being requested.
Eiffel is mostly that way, but in some places it has had some historical ambiguities that have context dependent semantics. The main one is the different expanded/reference attachment semantics; an
a := b statement sometimes creates an object, sometimes overwrites the contents of one, and sometimes reattaches a reference to an existing object. The SmartEiffel dialect have solved this at the cost of making some abstractions difficult (because of breaking the inheritance tree). This has always been a tricky side of Eiffel.
Implicit rules of conversion is another weak point that has had recent changes. Basic predefined types (integer, floating point values) had certain "magic" behind providing type conversion behind the scenes that was something that no user defined type could do, and was not explicit watching the interfaces of classes like INTEGER or REAL. The ISE proposal (included at ECMA) of removing magic convertions and allowing the class to explicitly add conversion routines seems a much better way to do it.
In conclusion... Eiffel has been very pythonic (much more than Python) but with some design issues that are being addressed.
Eiffel's solution for "Hello World" involves understanding creation features, and can hardly be as simple as the one-liner "Hello World" offered by many other languages.
Also I was interested in your comments on ISE/ECMA Eiffel conversions supporting the principle of "Explicit is better than implicit".
To me, conversions that enable one to write "date := [7, 6, 2006]" or "point_3d := [7, 6, 2006]" go against this principle. Sure, the detailed operation of the conversion is explicit, but it's not explicit at the place in the code where it is happening!
Similarly, "int := char" is not as explicit as "int := char.as_integer" or "int := char.as_digit" (according to which one the application needs).
Implicit redefinitions can create many problems in large systems, when adding new features in parent classes afterwards.
Hello World is not a rellay good example of a system, and the Eiffel version of it is conceptually simple, and creation procedures are a simple concept; that does not mean that creating the simplest possible app for a programmer that does not know the language is as simple (in fact, the simplest hello world I have seen is the one in INTERCAL, a language which is not simple).
I think that explicitly indicating class conversions is a step toward explicitness... forcing explicit calss like ".to_integer" is going further; but I believe it hampers readability, so the point where one should stop is debatable.