Eiffel is an ISO-standardized, object-oriented programming language designed to enable programmers to efficiently develop extensible, reusable, reliable software. Eiffel is used in academia as a language for teaching computer-programming principles. Eiffel is used in the finance, aerospace, health-care, video-gaming, and other industries as a development platform. Since 1985, many suppliers have developed Eiffel programming environments.
The Eiffel language's design is closely connected with the Eiffel programming method. Both are based on a set of principles, including design by contract, command-query separation, uniform-access principle, single-choice principle, open-closed principle, and option-operand separation.
Many concepts initially introduced by Eiffel have later found their way into Java, C#, and other languages. New language-design ideas, particularly through the ECMA/ISO standardization process, continue to be incorporated into the Eiffel language.
The key characteristics of the Eiffel language include:
Eiffel shuns coding tricks or coding techniques intended as optimization hints to the compiler. The aim is not only to make the code more readable, but also to allow programmers to concentrate on the important aspects of a program without getting bogged down in implementation details. Eiffel's simplicity is intended to promote simple, extensible, reusable, and reliable answers to computing problems. Compilers for computer programs written in Eiffel provide extensive optimization techniques, such as automatic in-lining, that relieve the programmer of part of the optimization burden while producing code whose efficiency is comparable to that of code written in C++.
The design goal behind the Eiffel language, libraries, and programming methods is to enable programmers to create reliable, reusable software modules. Eiffel supports multiple inheritance, genericity, polymorphism, encapsulation, type-safe conversions, and parameter covariance. Eiffel's most important contribution to software engineering is design by contract (DbC), in which assertions, preconditions, postconditions, and class invariants are employed to help ensure program correctness without sacrificing efficiency.
Eiffel's design is based on object-oriented programming theory, with only minor influence of other paradigms or concern for support of legacy code. Eiffel formally supports abstract data types. Under Eiffel's design, a software text should be able to reproduce its design documentation from the text itself, using a formalized implementation of the "Abstract Data Type".
EiffelStudio is an integrated development environment available under either an open source or a commercial license. It offers an object-oriented environment for software engineering. EiffelEnvision is a plug-in for Microsoft Visual Studio that allows users to edit, compile, and debug Eiffel projects from within the Microsoft Visual Studio IDE. EiffelStudio and EiffelEnvision are free for non-commercial use. Four other open source implementations are available: "The Eiffel Compiler" tecomp, Gobo Eiffel, SmartEiffel—the GNU implementation, based on an older version of the language—and Visual Eiffel.
Several other programming languages incorporate elements first introduced in Eiffel. Sather, for example, was originally based on Eiffel but has since diverged, and now includes several functional programming features. The interactive-teaching language Blue, forerunner of BlueJ, is also Eiffel-based. The Apple Media Tool includes an Eiffel-based Apple Media Language.
The Eiffel language definition is an international standard of the ISO. The standard was developed by ECMA International, which first approved the standard on 21 June 2005 as ECMA standard 367, Eiffel: Analysis, Design and Implementation Language. In June 2006, ECMA and ISO adopted the second version. In November 2006, ISO first published that version. The standard can be found and used free of charge on the ECMA site. The ISO version is identical in all respects except formatting.
Eiffel Software, "The Eiffel Compiler" tecomp and Eiffel-library-developer Gobo have committed to implementing the standard; Eiffel Software's EiffelStudio 6.1 implements some of the major new mechanisms—in particular, inline agents, assigner commands, bracket notation, non-conforming inheritance, and attached types. The SmartEiffel team has turned away from this standard to create its own version of the language, which they believe to be closer to the original style of Eiffel. Object Tools has not disclosed whether future versions of its Eiffel compiler will comply with the standard.
The standard cites the following, predecessor Eiffel-language specifications:
x.e might be the name of a file that defines a class called X.A class contains features, which are similar to "members", "attributes" or "methods" in other object-oriented programming languages. A class also defines its invariants, and contains other properties, such as a "notes" section for documentation. Eiffel's standard data types, such as ARRAY are all themselves classes, and collections are modified and accessed via features rather than special syntax. Moreover, unlike some "impure" object-oriented languages like Java, Eiffel eschews "basic data types" outside of its class system.
Every system must have a class designated as "root", with one of its creation procedures designated as "root procedure". Executing a system consists of creating an instance of the root class and executing its root procedure. Generally, doing so creates new objects, calls new features, and so on.
Eiffel has six basic executable instructions: assignment; object creation; routine call; conditional; iteration; and choice. Eiffel's control structures are strict in enforcing structured programming: every block has exactly one entry and exactly one exit.
Unlike many object-oriented languages, but like Smalltalk, Eiffel does not permit any assignment into fields of objects, except within the features of an object. Eiffel emphasizes information hiding and data abstraction, by requiring formal interfaces to data mutation. To put it in the language of other object-oriented programming languages, all Eiffel fields are "private", and "setters" are needed to modify values. An upshot of this is that "setters" can, and normally do, implement the invariants Eiffel provides syntax for.
HELLO_WORLDcreate
makefeature
make
do
print ("Hello, world!%N")
endend
In addition, the language supports a "check instruction" (a kind of "assert") and loop invariants.
The command-query distinction is important to the Eiffel method. In particular:
This design choice helps the readability of classes, by avoiding a cause of ambiguity about which routine will be invoked by a call. It also simplifies the language mechanism; in particular, this is what makes Eiffel's multiple inheritance mechanism possible.
Names can, of course, be reused in different classes. For example the "+" operator is defined in several classes: INTEGER, REAL, STRING, etc.
LIST [INTEGER] and LIST [WORD] are "generic derivations" of this class. Permitted combinations (with n: INTEGER, w: WORD, il: LIST [INTEGER], wl: LIST [WORD]) are
INTEGER resp. WORD are the "actual generic parameters" in these generic derivations.It is also possible to have 'constrained' formal parameters, for which the actual parameter must inherit from a given class, the "constraint". For example in
class HASH_TABLE [G, KEY -> HASHABLE]
HASH_TABLE [INTEGER, STRING] is valid only if STRING inherits from HASHABLE (as it indeed does in typical Eiffel libraries). Within the class, having KEY constrained by HASHABLE means that for x: KEY it is possible to apply to x all the features of HASHABLE, as in x.hash_code.
inherit clause at the beginning:
A
B
-- ... Rest of class declaration ...
redefine subclause of the inheritance clause, as in
A
redefine f, g, h end
B
redefine u, v end
Classes may be defined with deferred class rather than with class to indicate that the class may not be directly instantiated. Non-instantiatable classes are called abstract classes in some other object-oriented programming languages. In Eiffel parlance, only an "effective" class can be instantiated (it may be a descendent of a deferred class). A feature can also be deferred by using the deferred keyword in place of a do clause. If a class has any deferred features it must be declared as deferred; however, a class with no deferred features may nonetheless itself be deferred.
Deferred classes play some of the same role as interfaces in languages such as Java, though many object-oriented programming theorists believe interfaces are themselves largely an answer to Java's lack of multiple inheritance (which Eiffel has).
rename clauses. This is required in the case of multiple inheritance if there are name clashes between inherited features; without renaming, the resulting class would violate the no-overloading principle noted above and hence would be invalid.
TUPLE [name: STRING; weight: REAL; date: DATE]
["Brigitte", 3.5, Last_night]
t has been assigned the above tuple then t.weight has value 3.5.Thanks to the notion of assigner command (see below), dot notation can also be used to assign components of such a tuple, as in
t.weight := t.weight + 0.5
TUPLE [STRING, REAL, DATE]. (In some compilers this is the only form of tuple, as tags were introduced with the ECMA standard.)
The precise specification of e.g. TUPLE [A, B, C] is that it describes sequences of at least three elements, the first three being of types A, B, C respectively. As a result TUPLE [A, B, C] conforms to (may be assigned to) TUPLE [A, B], to TUPLE [A] and to TUPLE (without parameters), the topmost tuple type to which all tuple types conform.
For example, to execute the my_action block for each element of my_list, one would write:
my_list.do_all (agent my_action)
my_action only on elements satisfying my_condition, a limitation/filter can be added:
my_list.do_if (agent my_action, agent my_condition)
my_action and my_condition are routines. Prefixing them with agent yields an object that represents the corresponding routine with all its properties, in particular the ability to be called with the appropriate arguments. So if a represents that object (for example because a is the argument to do_all), the instruction
a.call ([x])
x, as if we had directly called the original routine: my_action (x). Arguments to call are passed as a tuple, here [x].
It is possible to keep some arguments to an agent open and make others closed. The open arguments are passed as arguments to call: they are provided at the time of agent use. The closed arguments are provided at the time of agent definition. For example, if action2 has two arguments, the iteration
my_list.do_all (agent action2 (?, y))
action2 (x, y) for successive values of x, where the second arguments remained set to y. The question mark ? indicates an open argument; y is a closed argument of the agent. Note that the basic syntax agent f is a short hand for agent f (?, ?, ...) with all arguments open. It is also possible to make the target of an agent open through the notation {T}? where T is the type of the target.
The distinction between open and closed operands (operands = arguments + target) corresponds to the distinction between bound and free variables in lambda calculus. An agent expression such as action2 (?, y) with some operands closed and some open corresponds to a version of the original operation curried on the closed operands.
The agent mechanism has been recently generalized to allow defining an agent without reference to an existing routine (such as my_action, my_condition, action2), through inline agents as in
require
not_void: s /= Void
do
s.append_character (',')
ensure
appended: s.count = old s.count + 1
end)
my_list.for_all (agent (x: INTEGER): BOOLEAN do Result := (x > 0) end)
valid_arguments of call. Several proposals for a purely static correction of this problem are available, including a language change proposal by Ribet et al..
once keyword in place of do. Non-first calls to a routine require no additional computation or resource allocation, but simply return a previously computed result. A common pattern for "once functions" is to provide shared objects; the first call will create the object, subsequent ones will return the reference to that object. The typical scheme is:
once
create Result.make (args)
-- This creates the object and returns a reference to it through `Result'.
end
Result in the example—can itself be mutable, but its reference remains the same.Often "once routines" perform a required initialization: multiple calls to a libraries can include a call to the initialization procedure, but only the first such call will perform the required actions. Using this pattern initialization can be decentralized, avoiding the need for a special initialization module. "Once routines" are similar in purpose and effect to the singleton pattern in many programming languages, and to the Borg pattern used in Python.
The ECMA specification allows variants of "once" (qualified by a keyword in parentheses, e.g. once (THREAD): once per process, once per thread, once per object. However, this is not yet fully implemented by current compilers (typically, only PROCESS and THREAD).
For example NEWSPAPER may conform to PUBLICATION, but INTEGER converts to REAL (and does not inherit from it).
The conversion mechanism simply generalizes the ad hoc conversion rules (such as indeed between INTEGER and REAL) that exist in most programming languages, making them applicable to any type as long as the above principle is observed. For example a DATE class may be declared to convert to STRING; this makes it possible to create a string from a date simply through
my_string := my_date
create my_string.make_from_date (my_date)
make_from_date in a convert clause at the beginning of the class.
As another example, if there is such a conversion procedure listed from TUPLE [day: INTEGER; month: STRING; year: INTEGER], then one can directly assign a tuple to a date, causing the appropriate conversion, as in
Bastille_day := [14, "July", 1789]
An Eiffel exception handler is defined using the rescue keyword. Within the rescue section, the retry keyword executes the routine again. For example, the following routine tracks the number of attempts at executing the routine, and only retries a certain number of times:
-- Connect to a server or give up after 10 attempts.
require
server /= Void and then server.address /= Void
local
attempts: INTEGER
do
server.connect
ensure
connected: server.is_connected
rescue
if attempts < 10 then
attempts := attempts + 1
retry
end
end
This example is arguably flawed for anything but the simplest programs, however, because connection failure is to be expected. For most programs a routine name like attempt_connecting_to_server would be better, and the postcondition would not promise a connection, leaving it up to the caller to take appropriate steps if the connection was not opened.
Eiffel's view of computation is completely object-oriented in the sense that every operation is relative to an object, the "target". So for example an addition
[1] a + b
[2] a.plus (b)
a, feature plus and argument b.
Of course [1] is the conventional syntax and usually preferred. Operator syntax makes it possible to use either form by declaring the feature (for example in INTEGER, but this applies to other basic classes and can be used in any other for which such an operator appropriate):
-- ... Normal function declaration...
end
Every class may in addition have one function aliased to "[]", the "bracket" operator, allowing the notation put (e: ELEMENT; key: STRING)
a [i, ...] as a synonym for a.f (i, ...) where f is the chosen function. This is particularly useful for container structures such as arrays, hash tables, lists etc. For example access to an element of a hash table with string keys can be written
number := phone_book ["JILL SMITH"]
a.x := v as this violates information hiding; you have to go for a setter command (procedure). For example the hash table class can have the function and the procedure
-- The element of key `key'.
-- ("Getter" query)
do
...
end
-- Insert the element `e', associating it with the key `key'.
-- ("Setter" command)
do
...
end
[4] phone_book.put (New_person, "JILL SMITH")
[5] phone_book ["JILL SMITH"] := New_person
phone_book ["JILL SMITH"] is a synonym for number := phone_book.item ("JILL SMITH")), provided the declaration of item now starts (replacement for [3]) with
item alias "[]" (key: STRING): ELEMENT assign put
put as the assigner command associated with item and, combined with the bracket alias, makes [5] legal and equivalent to [4]. (It could also be written, without taking advantage of the bracket, as phone_book.item ("JILL SMITH") := New_person.
note: The argument list of a's assigner is constrained to be: (a's return type;all of a's argument list...)
make, maKe and MAKE all denote the same identifier. See, however, the "style rules" below.Comments are introduced by -- (two consecutive dashes) and extend to the end of the line.
The semicolon, as instruction separator, is optional. Most of the time the semicolon is just omitted, except to separate multiple instructions on a line. This results in less clutter on the program page.
There is no nesting of feature and class declarations. As a result the structure of an Eiffel class is simple: some class-level clauses (inheritance, invariant) and a succession of feature declarations, all at the same level.
It is customary to group features into separate "feature clauses" for more readability, with a standard set of basic feature tags appearing in a standard order, for example:
feature -- Initialization
-- ... Declarations of initialization commands (creation procedures/constructors) ...
feature -- Access
-- ... Declarations of non-boolean queries on the object state, e.g. item ...
feature -- Status report
-- ... Declarations of boolean queries on the object state, e.g. is_empty ...
feature -- Element change
-- ... Declarations of commands that change the structure, e.g. put ...
-- etc.
end
Much of the documentation of Eiffel uses distinctive style conventions, designed to enforce a consistent look-and-feel. Some of these conventions apply to the code format itself, and others to the standard typographic rendering of Eiffel code in formats and publications where these conventions are possible.
While the language is case-insensitive, the style standards prescribe the use of all-capitals for class names (LIST), all-lower-case for feature names (make), and initial capitals for constants (Avogadro). The recommended style also suggests underscore to separate components of a multi-word identifier, as in average_temperature.
The specification of Eiffel includes guidelines for displaying software texts in typeset formats: keywords in bold, user-defined identifiers and constants are shown in italics, comments, operators, and punctuation marks in Roman, with program text in blue as in the present article to distinguish it from explanatory text. For example, the "Hello, world!" program given above would be rendered as below in Eiffel documentation:
class
HELLO_WORLD
create
make
feature
make
do
print ("Hello, world!")
end
end
It is possible for example to program machine- and operating-system level operations in C. Eiffel provides a straightforward interface to C routines, including support for "inline C" (writing the body of an Eiffel routine in C, typically for short machine-level operations).
Although there is no direct connection between Eiffel and C, many Eiffel compilers except one (Visual Eiffel) output C source code as an intermediate language, to submit to a C compiler, for optimizing and portability. The Eiffel Compiler tecomp can execute Eiffel code directly (like an interpreter) without going via an intermediate C code or emit C code which will be passed to a C compiler in order to obtain optimized native code. On .NET, the EiffelStudio compiler directly generates CIL (Common Intermediate Language) code. The SmartEiffel compiler can also output Java bytecode.