Skip to content

Types & Money

Storage is dynamically typed. The language layer is not. Declared shapes drive the typechecker, LSP, and executor drift handling.

Scalars

Id, Text, Int, Float, Bool, Money, Time, Duration, Bytes, Json, plus enum { … }, ref Other, and unknown.

Money (never float)

A money value is integer minor units + ISO 4217 currency — never a bare decimal float.

eel
49.99$usd    // canonical
49.99$       // bare form — only where schema currency is known (e.g. Money<usd>)
500$jpy
  • Cross-currency arithmetic and comparison are type errors. Convert explicitly.
  • In TypeScript, @eelden/client exposes an immutable Money class over bigint minors — use Money.minor(4999n, "USD") or Money.parse("49.99", "USD"). Accidental + with a number is structurally hard.

Enums

eel
status: enum { trial, paid, suspended }

users |> filter status == .paid

The lexer is context-free: . is always a token. The parser reads .ident as an enum literal in primary position and as field access in postfix position.

unknown, is, and as

Drifted or Json-derived values surface as unknown. They are never silently coerced.

eel
events |> filter metadata.amount is Money
events |> select { cents: metadata.amount as Money<usd> }
FormMeaning
expr is TypeBool predicate — runtime tag test
expr as TypeCast — fails per @strict / @loose

Rejected assertions: is ref X, is enum { … }, is unknown.

Time and duration

eel
orders |> filter placed_at > now() - 7d

now() is wall time from the injected clock (injectable in tests and wasm). Duration literals use d / h / m / s (e.g. 7d, 12h, 90m, 30s) and participate in arithmetic with Time.

Pre-alpha. Local-first. Stdlib-only Rust engine. Tenant concerns shifted left into the database.