Variables and Types in Eiffel
Learn about variables, data types, type conversions, and Design by Contract type safety in Eiffel with practical Docker-ready examples
In Eiffel, variables are called attributes when they belong to a class and locals when they exist within a routine. As a statically and strongly typed object-oriented language, every variable must have a declared type, and the compiler enforces type correctness at compile time. This catches entire categories of bugs before your code ever runs.
What makes Eiffel’s approach to variables distinctive is how it integrates with Design by Contract. You don’t just declare a variable’s type — you can also specify invariants about its valid values, and the runtime will verify those contracts. Combined with Eiffel’s void safety mechanism, which prevents null reference errors by distinguishing between attached and detachable types, the type system becomes a powerful tool for building reliable software.
In this tutorial, you’ll learn how to declare and use attributes and local variables, work with Eiffel’s built-in types, perform type conversions, and use constants. You’ll also see how Eiffel’s type system interacts with its contract mechanism.
Basic Types and Variable Declarations
Eiffel provides a set of fundamental types that form the building blocks for all programs. Attributes are declared in feature clauses, while local variables are declared in local sections within routines.
Create a file named variables.e:
class
VARIABLES
create
make
feature -- Initialization
make
-- Demonstrate basic variable types and declarations.
local
an_integer: INTEGER
a_real: REAL_64
a_character: CHARACTER
a_boolean: BOOLEAN
a_natural: NATURAL_32
an_integer_8: INTEGER_8
an_integer_64: INTEGER_64
do
-- Integer types
an_integer := 42
an_integer_8 := 127
an_integer_64 := 9_000_000_000
a_natural := 255
print ("=== Integer Types ===%N")
print ("INTEGER: " + an_integer.out + "%N")
print ("INTEGER_8: " + an_integer_8.out + "%N")
print ("INTEGER_64: " + an_integer_64.out + "%N")
print ("NATURAL_32: " + a_natural.out + "%N")
-- Floating-point types
a_real := 3.14159
print ("%N=== Floating-Point Types ===%N")
print ("REAL_64: " + a_real.out + "%N")
-- Character type
a_character := 'A'
print ("%N=== Character Type ===%N")
print ("CHARACTER: " + a_character.out + "%N")
-- Boolean type
a_boolean := True
print ("%N=== Boolean Type ===%N")
print ("BOOLEAN: " + a_boolean.out + "%N")
-- String type (reference type)
demonstrate_strings
demonstrate_type_conversions
demonstrate_constants
end
feature -- String demonstrations
demonstrate_strings
-- Show string variables and operations.
local
greeting: STRING
name: STRING
combined: STRING
do
greeting := "Hello"
name := "Eiffel"
combined := greeting + ", " + name + "!"
print ("%N=== String Type ===%N")
print ("greeting: " + greeting + "%N")
print ("name: " + name + "%N")
print ("combined: " + combined + "%N")
print ("Length of combined: " + combined.count.out + "%N")
end
feature -- Type conversions
demonstrate_type_conversions
-- Show how to convert between types.
local
i: INTEGER
r: REAL_64
s: STRING
b: BOOLEAN
do
print ("%N=== Type Conversions ===%N")
-- Integer to Real
i := 7
r := i.to_double
print ("Integer " + i.out + " to Real: " + r.out + "%N")
-- Real to Integer (truncates)
r := 9.81
i := r.truncated_to_integer
print ("Real " + r.out + " truncated to Integer: " + i.out + "%N")
-- Integer to String
i := 42
s := i.out
print ("Integer " + i.out + " to String: %"" + s + "%"%N")
-- Boolean to String
b := True
print ("Boolean to String: " + b.out + "%N")
end
feature -- Constants
Pi: REAL_64 = 3.14159265358979
-- The mathematical constant pi.
Max_attempts: INTEGER = 10
-- Maximum number of retry attempts.
App_name: STRING = "Eiffel Variables Demo"
-- Application name.
demonstrate_constants
-- Show constant usage.
do
print ("%N=== Constants ===%N")
print ("Pi: " + Pi.out + "%N")
print ("Max attempts: " + Max_attempts.out + "%N")
print ("App name: " + App_name + "%N")
end
end
This example covers the core built-in types: INTEGER (with its sized variants), REAL_64 for floating-point numbers, CHARACTER, BOOLEAN, and STRING. Notice how Eiffel uses underscore-separated numeric literals like 9_000_000_000 for readability, and how every type has an out feature that converts it to a string representation.
Void Safety and Detachable Types
One of Eiffel’s most important type system features is void safety — the compiler’s guarantee that you won’t encounter null reference errors at runtime. By default, all reference types are “attached,” meaning they can never be void (null). If a variable might legitimately have no value, you must declare it as detachable and check before using it.
Create a file named variables_void_safety.e:
class
VARIABLES_VOID_SAFETY
create
make
feature -- Initialization
make
-- Demonstrate void safety and detachable types.
do
print ("=== Void Safety ===%N")
demonstrate_attached
demonstrate_detachable
end
feature -- Attached vs Detachable
name: STRING
-- An attached attribute (cannot be Void after creation).
attribute
Result := "Default"
end
nickname: detachable STRING
-- A detachable attribute (can be Void).
demonstrate_attached
-- Show attached (non-void) attribute usage.
do
name := "Eiffel Developer"
print ("Name: " + name + "%N")
print ("Name length: " + name.count.out + "%N")
end
demonstrate_detachable
-- Show detachable (possibly void) attribute usage.
do
-- nickname is Void by default
print ("%N=== Detachable Types ===%N")
if attached nickname as n then
print ("Nickname: " + n + "%N")
else
print ("Nickname is Void (not set)%N")
end
-- Now assign a value
nickname := "Code Archaeologist"
if attached nickname as n then
print ("Nickname after assignment: " + n + "%N")
print ("Nickname length: " + n.count.out + "%N")
else
print ("Nickname is Void%N")
end
-- Demonstrate object test with detachable
check_value (nickname)
check_value (Void)
end
check_value (val: detachable STRING)
-- Check whether a detachable value is attached.
do
if attached val as v then
print ("Value is attached: " + v + "%N")
else
print ("Value is Void%N")
end
end
end
The attached keyword serves as both a type annotation and a runtime check. The pattern if attached nickname as n then is an “object test” — it checks whether the detachable reference holds a value, and if so, binds it to the local name n which is guaranteed to be non-void within that block. This eliminates the possibility of null pointer exceptions.
Attributes with Contracts
Eiffel’s type system works hand-in-hand with Design by Contract. You can declare class invariants that constrain attribute values, and the runtime will verify them after every public routine call.
Create a file named variables_contracts.e:
class
VARIABLES_CONTRACTS
create
make
feature -- Initialization
make
-- Demonstrate variables with contracts.
do
print ("=== Variables with Contracts ===%N")
set_temperature (72.0)
print ("Temperature: " + temperature.out + " F%N")
set_percentage (85)
print ("Percentage: " + percentage.out + "%%%N")
set_name ("Eiffel")
print ("Name: " + name + "%N")
print ("%N=== Expanded vs Reference Types ===%N")
print ("INTEGER is expanded (value type): " + percentage.out + "%N")
print ("STRING is reference type: " + name + "%N")
print ("%N=== Default Values ===%N")
print ("Default INTEGER: " + default_int.out + "%N")
print ("Default REAL_64: " + default_real.out + "%N")
print ("Default BOOLEAN: " + default_bool.out + "%N")
print ("Default CHARACTER: [" + default_char.out + "]%N")
end
feature -- Access
temperature: REAL_64
-- Current temperature in Fahrenheit.
percentage: INTEGER
-- A percentage value between 0 and 100.
name: STRING
-- A non-empty name.
attribute
Result := "Unknown"
end
feature -- Default value demonstrations
default_int: INTEGER
-- Shows default INTEGER value (0).
default_real: REAL_64
-- Shows default REAL_64 value (0.0).
default_bool: BOOLEAN
-- Shows default BOOLEAN value (False).
default_char: CHARACTER
-- Shows default CHARACTER value (null character).
feature -- Element change
set_temperature (a_temp: REAL_64)
-- Set temperature with contract.
require
reasonable_temp: a_temp >= -459.67 and a_temp <= 1000.0
do
temperature := a_temp
ensure
temperature_set: temperature = a_temp
end
set_percentage (a_value: INTEGER)
-- Set percentage with bounds checking.
require
valid_percentage: a_value >= 0 and a_value <= 100
do
percentage := a_value
ensure
percentage_set: percentage = a_value
end
set_name (a_name: STRING)
-- Set name with non-empty constraint.
require
name_not_empty: not a_name.is_empty
do
name := a_name.twin
ensure
name_set: name.is_equal (a_name)
end
invariant
valid_percentage: percentage >= 0 and percentage <= 100
valid_name: not name.is_empty
end
This example shows how contracts extend the type system. The percentage attribute is an INTEGER, but the class invariant guarantees it’s always between 0 and 100. The set_percentage routine’s precondition checks the input, the postcondition verifies the assignment, and the invariant ensures the constraint holds at all times. Notice also the distinction between expanded types (value types like INTEGER, REAL_64, BOOLEAN, CHARACTER) which have default values, and reference types (like STRING) which default to Void unless given an attribute body.
Running with Docker
Eiffel requires an ECF configuration file for compilation. Each example needs its own ECF file with the correct root class name.
| |
To run the void safety or contracts examples, change the root class in the ECF. For example, for variables_void_safety.e:
| |
Expected Output
For the basic variables example (variables.e):
=== Integer Types ===
INTEGER: 42
INTEGER_8: 127
INTEGER_64: 9000000000
NATURAL_32: 255
=== Floating-Point Types ===
REAL_64: 3.14159
=== Character Type ===
CHARACTER: A
=== Boolean Type ===
BOOLEAN: True
=== String Type ===
greeting: Hello
name: Eiffel
combined: Hello, Eiffel!
Length of combined: 14
=== Type Conversions ===
Integer 7 to Real: 7
Real 9.81 truncated to Integer: 9
Integer 42 to String: "42"
Boolean to String: True
=== Constants ===
Pi: 3.14159265358979
Max attempts: 10
App name: Eiffel Variables Demo
For the void safety example (variables_void_safety.e):
=== Void Safety ===
Name: Eiffel Developer
Name length: 16
=== Detachable Types ===
Nickname is Void (not set)
Nickname after assignment: Code Archaeologist
Nickname length: 18
Value is attached: Code Archaeologist
Value is Void
For the contracts example (variables_contracts.e):
=== Variables with Contracts ===
Temperature: 72 F
Percentage: 85%
Name: Eiffel
=== Expanded vs Reference Types ===
INTEGER is expanded (value type): 85
STRING is reference type: Eiffel
=== Default Values ===
Default INTEGER: 0
Default REAL_64: 0
Default BOOLEAN: False
Default CHARACTER: [ ]
Key Concepts
- All variables are typed — Eiffel is statically and strongly typed; every attribute and local must have a declared type, and the compiler enforces type correctness
- Expanded vs reference types — Expanded types (
INTEGER,REAL_64,BOOLEAN,CHARACTER) are value types with defaults (0, 0.0, False, null character); reference types (STRING, custom classes) default toVoid - Void safety — By default, reference types are “attached” and cannot be
Void; usedetachableto allow null values, andattached ... asobject tests to safely access them - Constants are class features — Constants are declared as attributes with an
= valueassignment directly in the feature declaration, not as local variables - Type conversions use feature calls — Convert with methods like
to_double,truncated_to_integer, andout(to string) rather than casting syntax - Contracts extend the type system — Preconditions, postconditions, and class invariants let you constrain variable values beyond what the type alone guarantees
- One class per file — Each
.efile contains exactly one class, with the filename matching the class name in lowercase (e.g.,variables.efor classVARIABLES) - String special characters use
%— Eiffel uses%Nfor newline,%Tfor tab, and%%for a literal percent sign, not backslash escapes
Running Today
All examples can be run using Docker:
docker pull eiffel/eiffel:latest
Comments
Loading comments...
Leave a Comment