Great IDE support
static analysis, first-class
A great static-analysis experience in the IDE is one of the goals — and the main target is PyCharm.
Immutability, pattern matching, comprehensions and hierarchical modules — written in the Python you already know, compiled to Erlang abstract forms through the same pipeline Gleam uses.
Read the syntax overview →def fib(n, current, next): match n: case 0: current case _ if n > 0: fib(n - 1, next, current + next) def fib(n): fib(n, 0, 1)
compiled · one function with a match instead of Erlang's multiple clauses
Eel is inspired by Gleam, but its goals and founding principles differ in a few important ways.
static analysis, first-class
A great static-analysis experience in the IDE is one of the goals — and the main target is PyCharm.
batteries, with methods
A stdlib that is pleasant to use, with OOP-style methods on the core data structures.
never mandatory
Static typing is fully optional. When you reach for it, it is there to make the code clearer and more expressive.
no second-class data
Maps, records, native records and the rest get the same level of support — and optional types make all of them convenient.
not an afterthought
Working with OTP matters as much as a good standard library.
BEAM ↔ CPython
One of the tasks is to bring the BEAM and CPython ecosystems closer, and to enable projects like Hornbeam.
Every construct below is compiled, not interpreted: the compiler turns this Python into Erlang abstract forms. Pick a category and read the code.
Numbers, floats, strings, atoms, booleans and None. Lists, tuples and dicts keep their Erlang shapes.
def values(): (42, 1.5, "hi", 'ok', True, False, None) def collections(): ([1, 2, 3], (1, 2), {'a': 1}, {"b": 2})
Python semantics for //, %, ** and ==; booleans compile to andalso and orelse.
def ops(): (7 // 2, -7 // 2, -7 % 2, 2 ** 10, 1 == 1.0, "a" < "b")
As the last expression, match and if return their branch value — no target variable needed.
def branch(n): # last expression: the branch value is the result match n: case 1 | 2: 'one or two' case big if big > 10: 'big' case _: 'other' def compare(a, b): # assigning works too, and reads the same if a < b: r = 'yes' else: r = 'no' r
Destructuring with a star, and list patterns inside match.
def head_tail(xs): head, *rest = xs (head, rest) def only_head(xs): match xs: case [h, *t]: r = (h, t) r
match receive() blocks for a message; send and recursion make the loop.
def worker(): match receive(): case ('ping', reply_to): send(reply_to, 'pong') worker() case 'stop': 'stopped'
def, lambda and closures — and arity is part of a function's identity.
def twice(f, x): f(f(x)) def inc(n): twice(lambda x: x + 1, n)
Applied once, like Python — a chain wraps from the inside out.
decorators.eeldef plus1(fn): lambda x: fn(x) + 1 @plus1 @plus1 def z(x): x def run(): z(3)
A class becomes a tagged tuple, and its constructor is inlined.
classes.eelclass Point(AtomPrefixed): x: int y: int def shift(p, dx): match p: case Point(x, y): q = Point(x + dx, y) q
Nested generators, filters, and dict comprehensions through .items().
def grid(): [x + y for x in [1, 2] for y in [10, 20]] def doubled(m): {k: v * 2 for k, v in m.items()}
The module is the path; imports are Python imports with Gleam's rules.
modules.eelfrom a.b.c import helper def call(): helper()
OOP-styling is a special feature of Eel: it makes the standard library pleasant to use, without complicating anything — outside the standard library, "OOP style" is not allowed.
name.startswith(prefix)prefix test, bytes includedname.strip()trim whitespace on both endsline.split(sep)split into a list of stringspath.replace(from, to)replace every occurrencesep.join(items)join a list back into one stringm.get(key)value or nil, never an exceptionm.keys()keys, in no particular orderm.items()pairs, for comprehensionsli.map(f)apply a function to every elementli.filter(pred)keep the elements that matchzip(li1, li2)pairs, like Python's zipenumerate(range(5))Python's enumerateIt compiles real modules today, however it is not something you put in production yet. Also, the code was written mostly with DeepSeek 4 Flash, in a vibe-coding loop.