Eiffel! It really is the most well-designed language I've used, but it's unfortunately also very obscure. There aren't a lot of libraries for it, the IDE is not super stable and online documentation is sparse and outdated (when looking for specific things you usually end up on websites from 1997-2004 where the syntax isn't even applicable anymore). It is super expressive and clean and favors encapsulation and information hiding, and it's at the same time almost as powerful as C++ but with a syntax that's infinitely better.
It is based on two major principles it pioneered/codified: design by contract and multiple inheritance. Design by contract is generic enough that it should be used in more languages (some have started implementing it). The gist of it is that every routine comes with preconditions (conditions that must be satisfied when it is called, for example: argument 1 must not be null, argument 2 must be > 0, the current object must be in a given state...), postconditions (conditions that must be satisfied when the routine ends, for example: the result will not be null, the result will be > 0 if the object is in this state but < 0 otherwise, the current object will now be in a given state...) and classes come with invariants (verified every time any of the class' routines are entered and exited to ensure that the object is always in a coherent state). Those are declared in a such a way that the compiler only considers them for development builds and removes them entirely during optimized builds, so it does not affect the performance of the final product but ensures that the program is always working as expected. It serves as both unit tests and documentation so it helps to avoid bugs and figure out usage of routines without looking at their implementation. Multiple inheritance is also a very cool and powerful feature that I haven't seen used in a convenient way in other languages I've worked with. When used cleverly, it avoids any and all code duplication, and it makes polymorphism extremely helpful. Not only do you have to write less code because you don't have to implement 10 variations of every routine for every entity (or 10 times the exact same routine, looking at you Java!), it also opens the way for specialized interfaces. For example, the LIST class (read-write) inherits from (among others) SEQUENCE, which is read-only. So if a routine takes a SEQUENCE as an argument, it means it will not modify it. There is also the READABLE_STRING family, which are read-only strings from which read-write STRINGs inherit. It's similar to const in C++ but it's built into the types themselves.
There is also feature export parameters which are immensely more powerful than most' languages private-protected-public visibilities. It allows you to make sets of features (members or routines) visible to types you explicitly declare. For example, the creation procedures of my key wrappers is only visible to the one class that's supposed to handle them, so I guarantees that all instances come from that manager and are thus correctly handled. You can also make it so only other instances of the same class can access a set of features so that e.g. you can tell an instance to copy the internal state of another one but that internal state will never be accessible from the outside.
Type inference is an extremely useful feature that I haven't seen in any other language yet (C++11 kind of has it but not in the same way). Instead of declaring an explicit type (be it on an attribute, a function's return type, routine arguments, generic parameters...), you can specify that it will be the same type as some other feature, or some other class' feature. For example: /// my_list : LIST[G] -- attribute declaration: a list of G
set_my_list(a_list : LIST[G]) -- Explicit argument type
set_my_list(a_list : like my_list) -- Inferred argument type ///
The second one will not need to be refactored if we decide to change the type of my_list. Those are resolved at compile-time so there is no downside. My implementation of keyboard input handling has several layers of cascading type inference that all have the same root (a NATURAL_16, i.e. 2-bytes unsigned int) virtual code representing the key being pressed, so all it took was to change the type of that attribute from NATURAL_16 to my custom enum type and it affected the whole stack. My enum type is more or less a descendant of NATURAL_16 so it could be used the same way if it needed to (and could be used transparently in place of an actual NATURAL_16 with no problem) but the compiler will not accept the opposite: you can't assign a regular NATURAL_16 to that field. That enum type only exports its constructors to one specific class (the enum proper), therefore all values must be named valid entries in the enum.
|