Beginner's Guide to Eiffel

  ·   14 min read

Introduction to Eiffel #

What is Eiffel? #

Eiffel is a general-purpose, object-oriented programming language known for its elegance, readability, and strong emphasis on software reliability. It features a unique approach to software development centered around Design by Contract (DbC), making it particularly suitable for building robust and maintainable systems. Unlike many other languages, Eiffel prioritizes clarity and correctness from the outset, leading to reduced development time and fewer bugs in the long run. It’s used in a variety of applications, from embedded systems and critical infrastructure to business applications and web development.

Why use Eiffel? #

Eiffel offers several compelling advantages for developers:

  • Increased Reliability: Design by Contract helps catch errors early in the development process, reducing the cost and effort of debugging. The language’s strong typing and rigorous design principles contribute to greater software stability.
  • Improved Maintainability: Eiffel’s clear syntax and consistent design make code easier to understand and modify. This reduces the long-term maintenance costs associated with software projects.
  • Enhanced Productivity: The built-in support for features like genericity and inheritance simplifies the development of complex systems, allowing developers to focus on the core logic rather than boilerplate code.
  • Formal Methods Integration: Eiffel’s design naturally aligns with formal methods, facilitating rigorous verification and validation of software.
  • Strong Community Support: While not as large as some other language communities, the Eiffel community is active and supportive, offering valuable resources and assistance to developers.

Eiffel’s key features: Design by Contract, Genericity, etc. #

Eiffel’s key features contribute significantly to its suitability for building reliable and maintainable software:

  • Design by Contract (DbC): This is Eiffel’s core strength. DbC involves specifying preconditions (what must be true before a routine is called), postconditions (what must be true after a routine completes), and invariants (conditions that must always hold true for a class). These contracts are checked at runtime, helping to quickly identify errors and prevent unexpected behavior.

  • Genericity: Eiffel supports genericity through the use of generic classes and routines. This enables the creation of reusable components that can work with different data types without modification, promoting code reuse and reducing redundancy.

  • Inheritance: Eiffel supports both single and multiple inheritance, allowing developers to create a hierarchy of classes and reuse existing code effectively.

  • Automatic Garbage Collection: Eiffel automatically manages memory, freeing developers from the burden of manual memory management and reducing the risk of memory leaks.

  • Strong Typing: Eiffel’s strong typing system helps catch type errors at compile time, leading to more robust and reliable code.

  • Multiple Dispatch: Eiffel supports multiple dispatch, allowing the system to select the most appropriate routine based on the types of all arguments, enhancing flexibility and code clarity.

Setting up your environment: Installing the compiler and IDE. #

The primary compiler for Eiffel is the EiffelStudio IDE, which provides a comprehensive environment for development, including the compiler, debugger, and other helpful tools. To set up your environment:

  1. Download: Go to the official Eiffel Software website (https://www.eiffel.com/) and download the latest version of EiffelStudio. Choose the version appropriate for your operating system (Windows, macOS, or Linux).

  2. Installation: Run the installer and follow the on-screen instructions. This will install the compiler, IDE, and necessary libraries.

  3. First Project: After installation, launch EiffelStudio. You can create a new project by selecting the appropriate option from the “File” menu. EiffelStudio provides tutorials and documentation to help you get started with creating and compiling your first Eiffel program. Refer to the EiffelStudio documentation for more detailed instructions and troubleshooting information. The IDE itself offers excellent interactive help and tutorials within its interface.

Basic Syntax and Concepts #

Classes and Objects #

In Eiffel, everything revolves around classes and objects. A class is a blueprint that defines the structure and behavior of objects. It specifies the data (attributes) and operations (features) that objects of that class will possess. An object is an instance of a class; it’s a concrete realization of the class’s blueprint. For example, a class CAR might define attributes like color, model, and speed, and features like accelerate and brake. Multiple CAR objects can exist, each with its own specific values for the attributes. Classes are declared using the class keyword, followed by the class name and its parent class (if any).

Attributes and Features (Methods) #

Attributes represent the data held by an object. They are declared within a class using the feature keyword followed by the attribute keyword. They represent the object’s state.

Features (often called methods in other languages) define the operations that can be performed on an object. They’re also declared using the feature keyword, but without the attribute keyword. They specify the object’s behavior. Features can take arguments and return values.

Example:

class CAR
feature
	color: STRING
	model: STRING
	speed: INTEGER
	
	accelerate (delta: INTEGER)
		do
			speed := speed + delta
		end
	
	brake (delta: INTEGER)
		do
			speed := speed - delta
			if speed < 0 then speed := 0 end
		end
end

Data Types: Integers, Booleans, Strings, etc. #

Eiffel provides a rich set of built-in data types, including:

  • INTEGER: Represents whole numbers.
  • REAL: Represents floating-point numbers.
  • BOOLEAN: Represents true or false values.
  • STRING: Represents sequences of characters.
  • CHARACTER: Represents single characters.
  • ARRAY: Represents ordered collections of elements of the same type.
  • LIST: Represents ordered collections of elements, potentially of different types (though often used homogeneously).

You can declare variables of these types within features or as attributes. Type safety is enforced by the compiler, preventing many errors during development.

Control Structures: if, then, else, loop, etc. #

Eiffel offers standard control structures for controlling the flow of execution:

  • ifthenelse: Conditional execution.
if speed > 100 then
    print ("Speed is high!")
else
    print ("Speed is okay.")
end
  • loopend: General-purpose loop. Often combined with across (iteration over collections) or until (loop until a condition is met).
loop
    -- some code
    if some_condition then exit end
end
  • across: Iterates over collections like arrays and lists.
across i in my_array loop
    print (i)
end

Creating and Using Simple Classes #

Let’s create a simple PERSON class and then use it:

class PERSON
feature
    name: STRING
    age: INTEGER
    
    set_name (new_name: STRING)
        do
            name := new_name
        ensure
            name_set: name = new_name
        end

    print_info
        do
            print ("Name: " + name + ", Age: " + age.out)
        end
end

class APPLICATION
feature
    main
        local
            p: PERSON
        do
            create p.set_name ("Alice")
            p.age := 30
            p.print_info
        end
end

This example shows attribute declaration, feature implementation, and the ensure clause in the set_name feature, illustrating a simple postcondition in Design by Contract. The APPLICATION class demonstrates how to create and use PERSON objects. Remember to compile and run this code using EiffelStudio. The output should be “Name: Alice, Age: 30”.

Design by Contract (DbC) #

Preconditions, Postconditions, Invariants #

Design by Contract (DbC) is a fundamental aspect of Eiffel programming. It’s a way to formally specify the behavior of software components by defining three types of clauses:

  • Preconditions: These are conditions that must be true before a routine (a method or function) is called. If a precondition is violated, it indicates a programming error in the caller of the routine. The routine is not obligated to do anything meaningful if the precondition is false; it can raise an exception, return an error code, or even halt execution.

  • Postconditions: These are conditions that must be true after a routine completes successfully. If a postcondition is violated, it indicates a bug within the routine itself.

  • Invariants: These are conditions that must always be true for a class. They define the consistent state that all instances of a class must maintain. Invariants are checked before and after every public feature (method) of the class.

How DbC Improves Code Quality and Reliability #

DbC significantly enhances code quality and reliability in several ways:

  • Early Error Detection: DbC helps catch errors early in the development process, during both testing and runtime. Precondition violations highlight problems in the calling code, while postcondition failures pinpoint bugs within the called routine. This reduces debugging time and effort.

  • Improved Code Clarity: Explicitly stating preconditions, postconditions, and invariants clarifies the intended behavior of code, making it easier to understand and maintain. It acts as a form of self-documentation.

  • Increased Modularity and Reusability: Well-defined contracts make components more independent and reusable. Developers can confidently use a component without needing to know its internal workings, relying solely on the contract for assurance.

  • Simplified Debugging: When an error occurs, DbC pinpoints the exact location of the problem—either in the caller (precondition violation) or in the called routine (postcondition violation)—making debugging significantly easier.

  • Enhanced Collaboration: DbC facilitates better collaboration among developers. The contracts act as a common understanding of the intended behavior, reducing ambiguities and misunderstandings.

Example of DbC in action #

Let’s consider a simple STACK class:

class STACK [G]
feature
    items: ARRAY [G]
    count: INTEGER

    put (item: G)
        require
            not full: count < items.count -- Precondition: Stack is not full
        do
            count := count + 1
            items[count] := item
        ensure
            item_added: count = old count + 1 -- Postcondition: Count increased by 1
            top_item: items[count] = item -- Postcondition: Top element is the added item
        end

    pop: G
        require
            not empty: count > 0 -- Precondition: Stack is not empty
        do
            Result := items[count]
            count := count - 1
        ensure
            item_removed: count = old count -1 -- Postcondition: Count decreased by 1
        end

invariant
    count_range: count >= 0 and count <= items.count -- Invariant: Count is always within bounds
end

This example shows preconditions (not full, not empty) ensuring the put and pop routines are used correctly. Postconditions (item_added, top_item, item_removed) verify the routines function as intended. The invariant (count_range) guarantees the count variable always maintains a valid state. If any of these conditions are violated during runtime, Eiffel will raise an exception, helping to immediately identify and correct the error.

Advanced Features #

Inheritance and Polymorphism #

Eiffel supports both single and multiple inheritance, allowing you to create a hierarchy of classes. Inheritance promotes code reuse and establishes a “is-a” relationship between classes. A subclass inherits the attributes and features of its superclass(es), and can extend or override them.

Polymorphism is the ability to treat objects of different classes uniformly through a common interface. In Eiffel, polymorphism is achieved through inheritance and dynamic binding. A feature call on an object is resolved at runtime based on the object’s actual class, not just its declared type. This allows you to write flexible and reusable code that can work with objects of various classes without needing to know their specific types.

Genericity #

Genericity allows you to create reusable components that can work with different data types without modification. You define a generic class or routine with type parameters, and the actual type is specified when an instance is created or the routine is called. This avoids code duplication and increases flexibility. For example, a generic STACK class can store elements of any type, not just a specific type like INTEGER or STRING.

Example:

class STACK [G] -- G is a generic type parameter
feature
    items: ARRAY [G]
    -- ... other features ...
end

Agent Mechanisms #

Agents are a powerful feature that allows you to defer the execution of a routine. An agent is essentially a delayed function call. You create an agent by specifying the routine and its arguments, and then execute the agent later. This is useful for situations such as event handling, asynchronous programming, and callbacks. Agents allow you to separate the scheduling of an action from its execution.

Working with Libraries #

Eiffel provides a rich set of libraries that offer pre-built components for various tasks. These libraries can significantly reduce development time and effort. To use a library, you generally need to:

  1. Include the library: You usually add an use clause to your code to import the necessary classes and routines from the library.

  2. Create instances: Create instances of classes provided by the library to utilize their features.

  3. Use features: Call features of the library classes to perform the desired actions.

EiffelStudio’s library management capabilities simplify this process. The IDE provides mechanisms for browsing, searching, and including libraries within your projects. The standard Eiffel libraries offer many useful components, including data structures, input/output operations, and more. Third-party libraries also extend Eiffel’s capabilities significantly. Refer to the Eiffel documentation and online resources to find and utilize relevant libraries for your projects. Properly understanding how to navigate and utilize the library ecosystem is essential for efficient Eiffel development.

Building and Running Your First Eiffel Program #

A simple ‘Hello, world!’ program #

The simplest Eiffel program prints “Hello, world!” to the console. Here’s the code:

class APPLICATION
feature
    main
        do
            io.putstring ("Hello, world!%N")
        end
end

This code defines a class named APPLICATION that contains a single feature, main. The main feature uses io.putstring to print the text to the console. %N inserts a newline character.

Compiling and Running Your Code #

To compile and run this code using EiffelStudio:

  1. Create a new project: In EiffelStudio, create a new project. Choose a suitable name and location.

  2. Create a new class: Add a new class to your project. Paste the “Hello, world!” code into the class’s editor.

  3. Compile: Click the compile button (usually a hammer icon). EiffelStudio will compile your code. If there are no errors, you’ll see a success message.

  4. Run: Click the run button (usually a play icon). This will execute the main routine, printing “Hello, world!” to the console’s output window.

Debugging Your Eiffel Code #

EiffelStudio provides a powerful debugger to help you find and fix errors in your code. Here’s how to use it:

  1. Set breakpoints: Click in the left margin of the editor next to the line numbers where you want the execution to pause. Breakpoints are indicated by a marker, often a red dot.

  2. Start debugging: Run your code in debug mode (often a button with a bug icon).

  3. Step through code: Use the stepping controls (step over, step into, step out) to move through your code line by line.

  4. Inspect variables: Use the debugger’s variable inspector to examine the values of variables at various points during execution. You can hover over variables in the code editor to see their values as well.

  5. Evaluate expressions: The debugger allows you to evaluate expressions at runtime to help understand the program’s state.

  6. Use the call stack: The call stack shows the sequence of routines that have been called, aiding in tracing execution flow and identifying the source of errors.

EiffelStudio’s debugger provides a user-friendly interface with all the tools required for effectively debugging Eiffel code. Refer to the EiffelStudio documentation for a more comprehensive guide to its debugging features. Familiarizing yourself with these features is crucial for efficient software development.

Next Steps and Resources #

Where to learn more about Eiffel #

This beginner’s guide provides a foundational understanding of Eiffel. To deepen your knowledge and explore more advanced topics, several resources are available:

  • Official Eiffel Software Website: The official website (https://www.eiffel.com/) is an excellent starting point. It offers documentation, tutorials, and links to other valuable resources.

  • EiffelStudio Documentation: EiffelStudio, the primary IDE for Eiffel, comes with comprehensive documentation that covers all aspects of the language and the IDE itself. Explore this documentation for detailed explanations of features and functionalities.

  • Online Courses and Tutorials: Several online platforms offer courses and tutorials on Eiffel programming. Search for “Eiffel programming tutorial” on platforms like YouTube, Udemy, and Coursera to find suitable learning materials.

  • Books on Eiffel: Various books are available that delve deeper into Eiffel’s features and best practices. Check online bookstores or libraries for relevant titles.

Eiffel Community and Forums #

Engaging with the Eiffel community is a great way to learn from experienced developers, ask questions, and share your knowledge. Here are some places to connect:

  • Eiffel Software Forums: The Eiffel Software website often hosts forums or discussion boards where developers can interact and address various questions.

  • Online Communities: Search for Eiffel-related groups or communities on platforms like Stack Overflow or Reddit. These communities provide a platform for asking questions, seeking help, and sharing expertise.

  • Meetups and Conferences: Check for local or online meetups or conferences focusing on Eiffel programming. Attending these events can provide opportunities to network with fellow developers and stay up-to-date on the latest trends.

Advanced Tutorials and Documentation #

Once you have a firm grasp of the basics, consider exploring these resources for advanced topics:

  • Eiffel Formal Methods Resources: Delve into the formal methods aspects of Eiffel. Learn how to use contracts for more rigorous software verification.

  • Advanced Eiffel Libraries: Explore the more advanced features offered within Eiffel’s extensive standard libraries and investigate third-party libraries to expand your development capabilities.

  • Design Patterns in Eiffel: Study how design patterns are implemented and utilized within the Eiffel framework for robust software design.

  • Large-Scale Eiffel Projects: Research examples of large-scale projects built with Eiffel to understand how the language is used in real-world applications. Analyzing these projects can provide valuable insights into best practices and efficient development strategies.

Consistent learning and engagement with the Eiffel community will solidify your understanding and enable you to build sophisticated and robust applications.