Skip to content

objectionary/eo

Repository files navigation

Pure Object-Oriented Language, Experimental

EO principles respected here DevOps By Rultor.com We recommend IntelliJ IDEA

mvn-linux PDD status Maintainability Maven Central codecov Lines-of-Code Hits-of-Code License FOSSA Status Quality Gate Status

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)

Quick Start

First, install Java SE and npm.

Then, install eoc:

npm install -g eolang@0.35.1

Then, 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 link

Then, run it:

eoc --easy --alone dataize app

You should see "Hello, world!" printed.

Simple Tutorial

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?

Backus-Naur Form

This is our EBNF of EO language:

Grammar

The PNG image was auto-generated. It's better to use ebnf/Eo.svg.

What's Next?

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.

Benchmark

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.csv

Then, 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"

Architecture

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.

How to Contribute

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 -Pqulice

You will need Maven 3.3+ and Java 11+ installed. Also, if you have xcop installed, make sure it is version 0.8.0+.

Contributors

contributors

Special thanks

We are using the YourKit Java Profiler to enhance the performance of EO components:

YourKit