User Tools

Site Tools


eiffel:style

Eiffel Style Guidelines

A Sense of Style

Observer basic style rules such as:

  • Well-chosen class and feature names. Underscores, not CamelCase.
  • Standard case convention (MY_CLASS, my_feature). (I still use first-upper-case for constants and onces representing shared objects, e.g. Avogadro, Error_window, but many people have shed that convention, and it is really optional – the others are not).
  • Proper use of tabs, spaces (before but not after an opening parenthesis, not before a closing parenthesis, before and after operators such as + and = ) etc.
  • Presence of header comments. No routine ever without a header comment.
  • Style of header comments. Should be terse (usually no need for articles, native speakers of Slavic languages welcome.) Every single argument of a routine should be named in the header comment, between a back quote and a normal quote. Every single one. For example, with display (v: T; f: FORMAT_SPECIFICATION), not “Display value with given format” but “Output `v’ using format `f’”.
  • Also for header comments, avoid repeating information that is already visible in the interface (contract view): use different words when it helps (“Output” if the feature name is `display’).
  • In these comments, usually no need to repeat what is already in the precondition, but there can be exceptions for emphasis (e.g. if the precondition says not t.has (x) the comment can still say “this routine is only applicable to x not already present; [New Line] For always applicable version, see `other_routine’”. This adds information not immediately derivable from the rest of the interface, by pointing to another routine.
  • Always fill `description’ note entries at the beginning of the class (or at the end for long entries, with a pointer at the beginning). Use multi-line (Verbatim) strings if necessary.
  • No need to end lines with semicolons (except in rare cases of syntactic ambiguity).
  • One instruction per line, although personally I write short conditionals on a single line, e.g. “if not t.is_empty then print (t) end”, for which 5 lines is a waste.
  • Any FIXME comment includes the nature of the problem that may have to be fixed, who wrote the comment, and when. (Better of course to have no such comment.)
  • Judicious use of parentheses when the precedence rules are not obvious, e.g. (a and b) or (c and d) for clarity. No need for superfluous C-style parentheses in conditionals: just if a = b then…

For variable naming, the following has been suggested (by Larry Rix):

  • a_var — feature arguments with exceptions for easily understood arguments like `i' or `x' or `y' (i.e. counters or typical math symbols).
  • l_var — local variables with same exceptions as rgument variables
  • ic_var — across-loop ITERATION_CURSORs
  • al_var — attachment tests where a local is needed (i.e. if attached {STRING} l_my_string as al_my_string thenend)
  • t_var — some instances of named-TUPLE-items, where one needs to be reminded in places beyond where the TUPLE is defined that the object is in fact a TUPLE, and not a typical class with attribute features. This “coding standard” rule for me is squishy.

Example

One may now read without ambiguity:

  • The arguments have the same semantics as the class features (e.g. `balances' and `minimum_balance'). There is no confusion.
  • The compiler is happy with the names in the named TUPLE, so there is no concern with any special modifier prefixes on them.
  • In the attachment test, the name of the attachment local with its “al_” prefix continues the immediate flow-of-understanding.
    • There is no confusion about `minimum_balance' vs `a_minimum_balance` vs `al_minimum_balance'. We understand both the semantics and scope of each.
    • `minimum_balance' is a class feature with controlled access by way of feature group.
    • `a_minimum_balance' is immediately recognizable to me as a routine argument, scoped to the routine only.
    • `al_minimum_balance' is immediately recognizable to me as an “attachment local”, scoped to the end of the “if … then … end”.
    • Semantically, we know the argument and the attachment local have the same purpose and meaning as `minimum_balance' because of the “like” declaration.
  • In the across loop:
    • We know that the argument `a_balances' is semantically identical to the `balances' class feature.
    • We know immediately that `ic_balances' is an ITERATION_CURSOR created by the compiler because of the across—I am easily reminded of this fact each time I read `ic_balances' without having to look back or elsewhere in code, which is a distraction.

We can scroll the top of this routine up beyond the top of the editor where we can no longer see it and then start reading the routine from the bottom and NOT loose my capacity to understand each identifier scope and perhaps not even its semantic (of which scope is a part for identifiers like locals, attachment locals, and iteration cursor locals).

In a book or other writing, what is more laborious? To understand what the writer is saying as-we-read—OR—having a barrage of footnotes to be constantly reminded or (worse) having to look away, loose our place, and look elsewhere to get a “reminder” of context? The more we look away from where we are reading, the more labor is involved, and (with code) the more likely we are to misunderstand, which leads to bugs.

Arguments about code looking like “natural language” might not be so compelling. Code is not a spy thriller, nor ought it be compared to it. It is not flowing English prose. It is code. It will always read like code, look like code, behave like code, operate as code, and have a singular purpose of communicating within the context of a larger system of code to accomplish the job needed by its owner through its creator. A spy novel does not have inheritance, polymorphism, and nor will we ever compile it to run it as machine instructions to tell a machine what to do. Such a comparison is thin at best.

Moreover—a spy novel is not terse, nor is it meant to be. It is meant to tell a story. Nor does it control airplanes, boats, cars, trains, MRIs, spacecraft, or other mission critical machines where human life hangs in the balance if that code goes off the rails. So—better not make too much of a comparison between the code we design and engineer and English prose.

We do not want code to be obfuscated as much as possible. Obfuscation leads to bugs. Eiffel is a carefully and thoughtfully, well tested, and well heeled method with a language and IDE to match. Understanding the method first leads one to quickly see how unnecessary “bad habit” conventions and styles are.

here is a danger in the mock code used as an example. For example: The nearness of `a_balances' to `balances' means that we can replace references to `a_balances' with `balances' in the routine body and the compiler will not complain. The instant we do so, I have created a bug that may be difficult to identify. This might be especially true if there was a reason for the code to make references to both `a_balances' and `balances'. However—even changing the argument name would leave such a situation of potential error unchanged.

CONCLUSION: We have used the prefixing of arguments, locals, attachment locals, and iteration cursor locals for a long time. They have spared us some grief and bugs. We have learned to rest on the good points of having and using them. These differing conventions do not mean we are prevented from using other libraries which use a completely different style! The only place where we ought to agree about convention and style is when we're on the same team, coding within the same system or library. Otherwise, we're all free to agree-to-disagree and do as we please, living happily in our own worlds. Have fun. Enjoy life. Be successful in all you do. My style won't stop you and yours will not stop me. This is a big pond with room enough for all!

(Adapted from Larry Rix, eiffel-users@google, 7 September 2018)

eiffel/style.txt · Last modified: 2018/09/07 16:18 by jonathan