EO (stands for Elegant Objects or ISO 639-1 code of Esperanto) is an object-oriented programming language based on π-calculus. We're aware of popular semi-OOP languages and we don't think they are good enough, including: Java, Ruby, C++, Smalltalk, Python, PHP, C#. All of them have something we don't tolerate:
- types (why?)
- static/class methods or attributes (why?)
- classes (why?)
- implementation inheritance (why?)
- mutability (why? and why not?)
- NULL (why?)
- global scope (why?)
- type casting (why?)
- reflection (why?)
- scalar types and data primitives
- annotations (why?)
- operators
- traits and mixins (why?)
- flow control statements (
for,while,if, etc)
First, install Java SE and npm.
Then, install eoc:
npm install -g eolang@0.35.1Then, start with a simple EO program in the app.eo file:
# Just prints hello.
[args] > app
Q.io.stdout > @
"Hello, world!\n"
Compile it like this (may take up to a minute or so):
eoc --easy linkThen, run it:
eoc --easy --alone dataize appYou should see "Hello, world!" printed.
In the example above, we create a new abstract object
named app, which has a single attribute named @. The object
attached to the attribute @ is a copy of the object stdout with
a single argument "Hello, world!". The object
stdout is also abstract.
It can't be used directly, a copy of it has to be created,
with a few required arguments provided.
This is how a copy of the object stdout is made:
Q.io.stdout
"Hello, world!\n"
The indentation in EO is important, just like in Python. There must be two spaces in front of the line in order to go to the deeper level of nesting. This code can also be written in a "horizontal" notation:
Q.io.stdout "Hello, world!"
Moreover, it's possible to use brackets in order to group arguments and avoid
ambiguity. For example, instead of using a plain string "Hello, world!"
we may want to create a copy of the object stdout with a more complex
argument: a copy of the object sprintf:
# Says hello to Jeff.
[] > app
Q.io.stdout > @
Q.tt.sprintf
"Hello, %s!"
* "Jeffrey"
Here, the object sprintf is also abstract.
It is being copied with two arguments: "Hello, %s!" and "Jeffrey".
This program can be written using horizontal notation:
+alias io.stdout
+alias tt.sprintf
# Also says hello to Jeff.
[] > app
stdout (sprintf "Hello, %s!" (* "Jeffrey")) > @
The special attribute @ denotes an object that is being
decorated.
In this example, the object app decorates the copy of the
object stdout and through this starts to behave like
the object stdout: all attributes of stdout become the
attributes of the app. The object app may have its own
attributes. For example, it's possible to define a new abstract object
inside app and use it to build the output string:
# Says hello to Jeff.
[] > app
Q.io.stdout (msg "Jeffrey") > @
[name] > msg
Q.tt.sprintf "Hello, %s!" (* name) > @
Now, the object app has two "bound" attributes: @ and msg. The attribute
msg has an abstract object attached to it, with a single "free" attribute
name.
This is how you iterate:
[args] > app
malloc.for > @
0
[x] >>
seq * > @
x.put 2
while
x.as-number.lt 6 > [i] >>
[i] >>
seq * > @
Q.io.stdout
Q.tt.sprintf *1
"%d x %1$d = %d\n"
x
x.as-number.times x
x.put
x.as-number.plus 1
true
This code will print this:
2 x 2 = 4
3 x 3 = 9
4 x 4 = 16
5 x 5 = 25
Got the idea?
This is our EBNF of EO language:
The PNG image was auto-generated. It's better to use ebnf/Eo.svg.
Join our Telegram group.
Watch video about EOLANG basics.
Read our blog, especially the section with recently published papers.
Learn XMIR, a dialect of XML, which we use to represent EO program: XSD and spec.
See the full collection of canonical objects: objectionary.
Read more about integration with Maven.
This is how many milliseconds were spent on different XSL stylesheets
during the execution of mvn install of the eo-runtime module:
to-java.xsl 49758 33.18%
classes.xsl 44024 29.35%
set-locators.xsl 13778 9.19%
set-original-names.xsl 9437 6.29%
attrs.xsl 8253 5.50%
data.xsl 7508 5.01%
anonymous-to-nested.xsl 5818 3.88%
tests.xsl 5728 3.82%
package.xsl 5682 3.79%
The results were calculated in this GHA job on 2026-05-06 at 09:53, on Linux with 4 CPUs. The total is 149986 milliseconds. We show only the first 16 most expensive XSL stylesheets.
You can run this benchmark locally with the following commands.
First, to generate the measures.csv file:
mvn clean install --errors --batch-mode -Deo.xslMeasuresFile=measures.csvThen, to generate the report:
awk -F ',' '{ a[$1]+=$2; s+=$2; } END { for (k in a) \
printf("%s.xsl\t%d\t%0.2f%%\n", k, a[k], 100 * a[k]/s)}' \
eo-runtime/measures.csv | sort -g -k 2 | tail -16 | column -t | head "-16"The language is formally grounded in
Ο-calculus,
a mathematical model where every entity is an object
with named attributes, and objects are formed by
applying other objects to free attributes.
Unlike languages based on
lambda calculus
(e.g. Haskell)
or class-based models
(e.g. Java,
C++),
EO has no types, no classes, no static methods,
no implementation inheritance, no NULL, and no operators.
Everything a programmer needs to express is an object,
and the only way to reuse behavior is through decoration:
an object's Ο (@) attribute points to another object
whose attributes are transparently forwarded,
replacing class inheritance entirely.
The compiler pipeline uses XMIR (XML Intermediate Representation) as its sole pivot format between parsing and code generation. Unlike traditional compilers that manipulate an in-memory AST (as in GCC, LLVM, or javac), every compilation artifact in EO is a serializable XML document governed by an XSD schema. This makes every intermediate state inspectable with standard XML tools and independently testable.
All transformations at both parse-time normalization
and transpile-time code generation are implemented as
pipelines of XSLT 2.0 stylesheets
rather than visitor passes written in the host language.
In most compilers transformations are encoded as Java/C++ AST visitors
(e.g. Eclipse JDT,
Roslyn);
here each transformation step is a self-contained .xsl file
that can be tested, measured, and replaced without touching Java code.
The benchmark section above reflects the measurable cost of each
stylesheet.
The build system integration uses
Apache Maven as the compilation driver.
EO is compiled as part of a Maven lifecycle:
the eo-maven-plugin module exposes mojos
(parse, assemble, transpile, etc.)
that run during generate-sources and process-sources phases,
integrating EO into the existing Java toolchain
without a separate build toolβunlike languages with custom toolchains
such as Rust (cargo)
or Go (go build).
External EO objects are resolved from
Objectionary,
a Git-hosted registry of canonical objects,
not from a binary artifact repository like
Maven Central or
npm.
The MjPull mojo fetches missing .eo sources from Objectionary
at build time and caches them locally,
so dependencies are always available as readable source,
not opaque compiled artifacts.
The standard library is written in EO itself.
Objects such as bytes, number, string, tuple, and seq
live in eo-runtime/src/main/eo/ as plain .eo files,
compiled by the same pipeline and shipped in the runtime JAR.
This self-hosting constraint forces the compiler and runtime
to be correct for the subset of EO used by the standard library,
and it means a new contributor can read and modify
primitive behavior without touching Java.
Fork repository, make changes, then send us a pull request.
We will review your changes and apply them to the master branch shortly,
provided they don't violate our quality standards.
To avoid frustration, before sending us your pull request
please run full Maven build:
mvn clean install -PquliceYou will need Maven 3.3+ and Java 11+ installed.
Also, if you have xcop installed, make sure it is version 0.8.0+.
We are using the YourKit Java Profiler to enhance the performance of EO components:

