Eiffel and Delphi compared

The following rant has been brought to you after I, again, was not able to adapt an existing component, but had to resort to copy/paste reuse.

THIS IS A WORK IN PROGRESS...

Delphi has become complex

Delphi is a very complex language. What started in Turbo Pascal 1.0 as a relatively simple language, has become an hideous Swiss army knife of ill-considered extensions and obsolete constructs.

Eiffel is a very simple, very clean language. It is easy to read, even for novices, and easy to learn.

Open/closed principle

Eiffel is built upon the open/closed principle. This principle states that:

Modules should be both open and closed:

  • A module is said to be open if it is still available for extension. For example, it should be possible to expand its set of operations or add fields to its data structures.
  • A module is said to be closed if it is available for use by other modules. This assumes that the module has been given a well-defined, stable description (its interface in the sense of information hiding). At the implementation level, closure for a module also implies that you may compile it, perhaps store it in a library, and make it available for others (its clients) to use. In the case of a design or specification module, closing a module simply means having it approved by management, adding it to the project's official repository of accepted software items (often called the project baseline), and publishing its interface for the benefit of other module authors.

Delphi only knows the closed principle. Anyone who ever tried to change an existing component knows the frustration when he detects that most of the stuff he needs to access is in a private section or cannot be overriden.

There are many, many ways to hide things in Delphi. To bury them, so noone can ever touch them. You can mark a routine or a property as private of course. But you can also define types and routines in the body of a unit so they're not accessible either.

In Delphi, realistically speaking, there is only one way of reuse, and that is the Copy/Paste principle. Except if the class you want to change is written by the all-knowing all-foreseeing designer. He will have made all the routines you ever want to access either accessible or virtual. The same for properties. Give him a thumb-ups from me when you meet him. If he exists, because I never had the privilege to meet him, nor see any of his code.

Here's the list of how you can cast your Delphi code into concrete:

  1. Put your attributes and functions in the private section of your class. Your solution is a quick hack anyway, so it's better to hide it.
  2. Don't declare a procedure of function virtual. Really, no one wants to override what you did anyway.
  3. Use enumerated types. No one ever will have a need to extend this type.
  4. Write procedures and functions in the implementation of your unit, and don't export them. They're not good enough to be reused anyway.

On the importance of resuse, see also Daniel Moissett's comments.

Memory management

Eiffel has garbage collection. No more memory leaks. In Eiffel you typically do not concern yourself with memory management. You might be concerned by releasing resources as soon as possible such as closing a file instead of postponing the close until the object becomes garbage collected, but that's all. There are some settings of the garbage collector you sometimes want to set, but these occassions are rare.

In Delphi you're in control. And don't make a mistake, else your application will leak memory. So your typical code is sprinkled with try...finally handlers.

And sometimes you don't have to release the memory, but it all depends. Take for example strings. Strings are garbage collected in Delphi. Except if they are PChars.

And you don't have to free an object if it inherits from IUknown. But woe, if it inherits from IUnknown and you still free it.

Language differences

Perhaps some of this needs to be moved to a different page like Eiffel for Delphi programmers?

  1. In Eiffel there are only classes and objects.
  2. Where to place your class? In Delphi your class can go in a unit or in the program file. And a unit may (sometimes must) contain more than one class. In Eiffel this is simple: a single class is placed in a single file. Every file contains just one class. Eiffel doesn't have the concept of a unit. It has the concept of a cluster, but a cluster is simply all the classes in a directory. A cluster can be compared to an entry in Delphi's search path.
  3. No .dpr or main program file.
  4. Do you have to initialize a variable? It depends:
    1. Global variables have a default value, except when your application is DLL or BPL it seems...
    2. Member variables (attributes) have a default value.
    3. Variables declared on the stack have no default value.
  5. Do you have to free/dispose a variable? It depends:
    1. Strings are reference counted, and don't have to be freed.
    2. Non objects structures allocated with GetMem have to be freed with FreeMem.
    3. COM objects (inheriting from IUnknown) are reference counted, and don't have to be freed.
    4. Delphi classes have to be freed.
  6. Typecasts and function calls look the same in Delphi.
  7. It matters if you have a function or a variable in a class. The Assigned function only works if you give it a variable, not if you give it a function. This breaks abstraction. This is due to the var keyword.
  8. You can ignore the result, can have disastrous results, and you're not warned.
  9. Even if a class member is protected, you can still read (and change it I suppose) if the classes are defined in the same unit.
  10. Delphi supports overloading. So what a routine does, cannot be understood from its name, but you have to look at its parameters as well.

Why is Delphi the way it is?

The simple answer is: because it is missing Design-by-Contract. Many programming languages share many of Delphi's shortcomings because of this. In order to control what a client may do with an object, programmers feel they have no other option than to hide their code.

The other reason is that the compiler writers felt they had to leave the hard stuff to programmers. Whole system analysis is extremely beneficial, but hard on the compiler writer.