- Tags:
- eiffel
- Lua
- Glue languages
An essay inspired by this short youtube video: Why learn LUA ?
(Caution: AI generated content, watch out for hallucinations !)
Introduction #
On the surface, Eiffel and Lua are about as different as two programming languages can be. Eiffel is a statically typed, compiled, pure object-oriented language built around Design by Contract, with a type system strict enough to prove void safety at compile time. Lua is a dynamically typed scripting language that fits in a few hundred kilobytes and has no classes at all. Eiffel wants you to write specifications. Lua wants to get out of your way. One is associated with banking, aerospace and defence. The other powers game mods, Neovim configurations, and nginx plugins.
Look closer, though, and the two start to look like distant cousins. They share origins, aesthetics, and a set of convictions about what a language should leave out. Those shared convictions are less obvious than their differences, and more interesting.
Born outside the mainstream, shaped by a single vision #
Neither language came out of a big corporate lab or a standards committee. Eiffel was designed by Bertrand Meyer in the mid-1980s and developed at his company, Interactive Software Engineering. Lua was created in 1993 at Tecgraf, a computer graphics group at the Pontifical Catholic University of Rio de Janeiro, by Roberto Ierusalimschy, Luiz Henrique de Figueiredo and Waldemar Celes. Brazil's trade barriers at the time made imported software hard to get, so Tecgraf built its own tools.
Both languages came from practical need rather than academic curiosity. Meyer wanted a language he could actually build reliable software in. The Lua team wanted to replace two small in-house data-description languages (one called SOL, "sun" in Portuguese, which is why its successor became Lua, "moon"). Both are named in a language other than English, and each name suggests something lasting: a feat of engineering and a celestial body.
Most importantly, both have been guarded by a very small group of designers who have been willing to say no. Lua's authors are famous for making compatibility-breaking changes between versions when they believe the design will be better for it, and for refusing features that don't earn their place. Eiffel has been shaped for four decades by Meyer's vision, and it has kept its identity where so many languages have grown by accumulating other languages' features. Each also has a canonical book by its designer that doubles as a design manifesto: Object-Oriented Software Construction for Eiffel, Programming in Lua for Lua.
A family resemblance in syntax #
Put the two side by side and the resemblance is immediate:
factorial (n: INTEGER): INTEGER
-- Factorial of `n'
require
non_negative: n >= 0
do
if n = 0 then
Result := 1
else
Result := n * factorial (n - 1)
end
end
-- Factorial of n
function factorial(n)
assert(n >= 0, "non_negative")
if n == 0 then
return 1
else
return n * factorial(n - 1)
end
end
Both use -- for comments. Both close blocks with keywords, not braces, using if ... then ... elseif ... else ... end. Both spell logical operators as words: and, or, not. Both make semicolons optional and rarely used. Both belong to the Algol and Pascal tradition that treats code as something to be read aloud, as opposed to C's tradition of dense punctuation. A programmer fluent in one can read the other's control flow almost without effort.
The resemblance even extends to looping by exit condition. Eiffel's repeat ... until both think of a loop in terms of when it stops, where C thinks in terms of when it continues.
The similarities stop at assignment and equality, which point in opposite directions. Eiffel uses = and ==. There is also a small irony around the tilde: Lua writes inequality as ~=, while in Eiffel
One concept to rule them all #
The deepest similarity is architectural. Each language is built around a single unifying structure and refuses to add others.
In Eiffel, that structure is the class. There are no separate modules, namespaces, or packages at the language level, no free functions, and no static members. A class is the module and the type at the same time, and everything lives in one. Where other languages would use a global or a static, Eiffel uses
In Lua, it is the table. Arrays, dictionaries, records, objects, modules, namespaces, and the global environment are all tables. There is no separate array type, no struct, no class declaration. Since Lua 5.2, even global variables are not really a language feature: a free name like x is just shorthand for _ENV.x, a field access on an ordinary table.
That gives the two languages a surprising shared property. Neither has global variables as a real concept. Eiffel gets there by forbidding them, and Lua gets there by reducing them to something else, but in both cases the designers decided global state should not be a primitive.
The same instinct shows up in each language's refusals. Eiffel has no method over, on the principle that one name should mean one thing within a class. Lua has no classes, no inheritance keyword, and no interfaces. It offers metatables and trusts programmers to build whatever object model they need. Meyer's maxim that a language should provide one good way to do each thing, and the Lua team's motto of providing "mechanisms, not policies," sound opposed but come from the same distrust of redundancy.
Objects as a do-it-yourself kit #
The contrast is sharpest in how each language gets objects. In Eiffel, the class is the language: you cannot write a line of Eiffel that doesn't live inside one. Lua has no classes at all, yet it supports object-oriented programming comfortably, because objects can be assembled from parts the language already provides. An object is just a table holding its state. A "class" is another table holding shared methods, attached to its instances through a metatable whose __index field sends failed lookups to it. Inheritance is the same trick applied again, chaining one class table to another. The only concession Lua makes in syntax is the colon: account:deposit(50) is shorthand for account.deposit(account, 50), passing the object as a hidden first argument called self, much as Eiffel provides
local Account = {}
Account.__index = Account
function Account.new(balance)
return setmetatable({balance = balance}, Account)
end
function Account:deposit(amount)
self.balance = self.balance + amount
end
Because the object model is a convention rather than a rule, Lua programmers have built many of them: single and multiple inheritance, mixins, prototype-style delegation, and full class libraries such as middleclass. This is "mechanisms, not policies" at its clearest. Eiffel decides once, in the language definition, what a class is and how inheritance works, down to renaming and redefinition, so the compiler can check it. Lua hands over the pieces and lets each program decide. Both languages refuse to have two ways of structuring code. They just disagree about whether that one way should be built into the language or left to the programmer to build.
Uniform access, arrived at from opposite ends #
One of Eiffel's founding ideas is the Uniform Access Principle. A client writing
Lua doesn't state this principle, but its metatables make it possible. Using the __index metamethod, a field that looks like a plain stored value (account.balance) can be computed on demand, looked up in a prototype, or fetched lazily, and the caller cannot tell the difference. Eiffel guarantees uniform access through language rules. Lua allows it through a runtime mechanism. Both reach the same conclusion: how a value is produced is the supplier's business, not the client's.
Exceptions without try #
Neither language has try/ catch blocks, and in both cases this is deliberate.
Eiffel attaches exception handling to the routine. A routine may have a
Lua also works at the function level. error raises an error, and pcall ("protected call") runs a function and reports whether it succeeded. There is no block-level catching and no exception type hierarchy in the core language. Just a function that either completes or doesn't.
The two approaches feel different in practice. Eiffel's is disciplined and contract-driven, while Lua's is minimal and pragmatic. But they share a structural choice that sets them apart from Java, C++ and Python: the function is the boundary of failure, and arbitrary regions of code cannot be wrapped in handlers.
Concurrency without threads and locks #
Neither language puts preemptive threads and locks at the centre of its concurrency model.
Lua's core offers coroutines: cooperative, explicitly yielding threads of control that share no parallelism and need no locks. For many of Lua's best-known uses, including game scripting, event loops, and OpenResty request handling, coroutines are the whole story.
Eiffel's answer is SCOOP (Simple Concurrent Object-Oriented Programming). Objects are declared
The mechanisms could hardly be more different. The shared belief is that raw shared-memory threading is too dangerous to hand to programmers as the default.
Both are married to C #
Each language uses C as its foundation, in mirror-image ways.
Lua is written in clean, portable ANSI C and is designed to be embedded. Its entire C API revolves around a virtual stack through which host programs and Lua scripts exchange values. Lua lives inside C programs.
EiffelStudio compiles Eiffel to C, then hands the result to a C compiler. Eiffel code can call C directly through
So one language is a guest in C programs and the other uses C as its compilation target and portability layer. Both owe their remarkable portability to that choice, and both give serious programmers a clean way to reach the metal when they need to.
Closures that arrived by different roads #
Lua has had first-class functions with proper lexical closures, which it calls upvalues, since early on. Eiffel added agents later in its life: objects that wrap a routine call, with some arguments supplied now ("closed") and others left for later ("open"). An Eiffel agent such as for ... in over iterator functions and Eiffel's
Where the resemblance ends, and why that matters #
None of this means the languages are secretly the same. Eiffel's defining feature, contracts checked as preconditions, postconditions and class invariants, has no real equivalent in Lua beyond assert. Lua's defining feature, dynamic runtime flexibility that lets a program rewrite its own objects and environments as it runs, is precisely what Eiffel's type system exists to prevent. Eiffel ignores case in identifiers, and Lua is case-sensitive. Eiffel is standardised through ECMA and ISO, and Lua is defined by its reference implementation and manual. Eiffel's void safety treats absence as something to eliminate statically. In Lua, nil is everywhere, and assigning nil to a table field is how you delete it.
What the similarities show is that design temperament is independent of type discipline. Both languages were built by small groups with strong, long-held convictions, and both argue that a language is defined as much by what it leaves out as by what it includes. Both chose one central abstraction and refused to multiply others. Both chose readable keywords over punctuation, functions over blocks as the boundary of failure, and structured concurrency over raw threads. Both, in their very different niches, have outlasted many more fashionable contemporaries.
If you asked Bertrand Meyer and the Lua team to design a language together, they would probably disagree about almost every feature. They might agree completely on how short the final list should be.
Lua's home in the games industry #
Lua's embeddability is the main reason it found its biggest audience in video games. A game engine is typically written in C or C++ for speed, but the logic that changes most often, such as level scripts, enemy behaviour, dialogue and user interfaces, benefits from a language that designers can edit without recompiling the engine. Lua was almost made for that job: its interpreter is tiny, fast enough for real-time use (especially with LuaJIT), easy to sandbox, and released under the permissive MIT licence, so studios can ship it without legal worry. LucasArts adopted it for Grim Fandango in 1998, and it spread from there. World of Warcraft uses Lua for its user-interface add-ons, which introduced a generation of players to programming. Roblox is built on Luau, a Lua derivative with gradual typing, and millions of young developers write games in it. Engines and frameworks such as LÖVE, Defold and Solar2D use Lua as their primary language. Eiffel's industrial home could hardly be more different, lying in finance, defence and other domains where a failure is expensive and correctness has to be argued for, not just tested. Yet the two niches reflect the same underlying trait: each language became indispensable in the one place where its designers' central priority, embedding in Lua's case and reliability in Eiffel's, matters most.
Lua keywords #
Lua (version 5.4) has 22 reserved keywords:
| and | break | do | else | elseif | end |
| false | for | function | goto | if | in |
| local | nil | not | or | repeat | return |
| then | true | until | while | | |
Lua Notes #
gotowas introduced in Lua 5.2, so Lua 5.1 has 21 keywords. LuaJIT, although based on 5.1, also acceptsgoto.- Lua is case-sensitive, so
AndorENDare valid identifiers rather than keywords. - By convention, names beginning with an underscore followed by uppercase letters (such as
_VERSIONor_ENV) are reserved for internal use, but they are not keywords.