East Language
A statically typed, expression-based programming language embedded in TypeScript. Write programs using a fluent API, compile to portable IR.
Quick Start
typescript
1// Types and helpers are direct imports (NOT East.IntegerType)
2import { East, IntegerType, StringType, ArrayType, NullType } from "@elaraai/east";
3
4// 1. Define platform functions (East.platform)
5const log = East.platform("log", [StringType], NullType);
6const platform = [log.implement(console.log)];
7
8// 2. Define East function (East.function)
9const sumArray = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
10 const total = $.let(arr.sum());
11 $(log(East.str`Sum: ${total}`));
12 $.return(total);
13});
14
15// 3. Compile and execute (East.compile)
16const compiled = East.compile(sumArray, platform);
17compiled([1n, 2n, 3n]); // logs "Sum: 6", returns 6n
Decision Tree: What Do You Need?
Task → What do you need?
│
├─ Create East Expressions (East.*)
│ ├─ From TypeScript value → East.value(tsValue) or East.value(tsValue, Type)
│ ├─ String interpolation → East.str`Hello ${name}!`
│ └─ Inside function body → $.let(value), $.const(value)
│
├─ Define a Type (import from package, NOT East.*)
│ ├─ Primitive → IntegerType, FloatType, StringType, BooleanType, DateTimeType, BlobType, NullType
│ ├─ Collection → ArrayType(T), SetType(K), DictType(K, V), RefType(T)
│ ├─ Compound → StructType({...}), VariantType({...}), RecursiveType(...)
│ ├─ Function → FunctionType<I, O>, AsyncFunctionType<I, O>
│ └─ Patch → PatchType(T) (compute patch type for any East type)
│
├─ Create TypeScript Values for East Types
│ ├─ NullType → null
│ ├─ BooleanType → true, false
│ ├─ IntegerType → 42n (bigint literal)
│ ├─ FloatType → 3.14 (number literal)
│ ├─ StringType → "hello"
│ ├─ DateTimeType → new Date("2025-01-01T00:00:00Z")
│ ├─ BlobType → new Uint8Array([...])
│ ├─ ArrayType(T) → [1n, 2n, 3n]
│ ├─ SetType(K) → new Set([1n, 2n, 3n])
│ ├─ DictType(K, V) → new Map([["a", 1n], ["b", 2n]])
│ ├─ StructType({...}) → { field1: value1, field2: value2 }
│ ├─ VariantType({...}) (use helpers from package)
│ │ ├─ Option type → some(value), none ← preferred for Option/Maybe
│ │ └─ Other variants → variant("caseName", value)
│ └─ RefType(T) (use helper from package) → ref(value)
│
├─ Write a Function (East.*)
│ ├─ Synchronous → East.function([inputs], output, ($, ...args) => { ... })
│ └─ Asynchronous → East.asyncFunction([inputs], output, ($, ...args) => { ... })
│
├─ Compile and Execute (East.*)
│ ├─ Sync function → East.compile(fn, platform)
│ └─ Async function → East.compileAsync(fn, platform)
│
├─ Use Platform Effects (East.*)
│ ├─ Sync effect → East.platform("name", [inputs], output).implement(fn)
│ └─ Async effect → East.asyncPlatform("name", [inputs], output).implement(fn)
│
├─ Block Operations ($)
│ ├─ Variables → $.let(value), $.const(value), $.assign(var, value)
│ ├─ Execute → $(expr), $.return(value), $.error(message)
│ ├─ Control Flow → $.if(...), $.while(...), $.for(...), $.match(...)
│ └─ Error Handling → $.try(...).catch(...).finally(...)
│
├─ Expression Operations
│ ├─ Boolean
│ │ ├─ Logic → .and($=>), .or($=>), .not(), .ifElse($=>,$=>)
│ │ ├─ Bitwise → .bitAnd(), .bitOr(), .bitXor()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne()
│ ├─ Integer
│ │ ├─ Math → .add()/.plus(), .subtract()/.sub()/.minus(), .multiply()/.mul()/.times(), .divide()/.div(), .remainder()/.mod()/.rem()/.modulo(), .pow(), .abs(), .sign(), .negate(), .log()
│ │ ├─ Convert → .toFloat()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne(), .lessThan()/.less()/.lt(), .greaterThan()/.greater()/.gt(), .lessThanOrEqual()/.lessEqual()/.lte()/.le(), .greaterThanOrEqual()/.greaterEqual()/.gte()/.ge()
│ ├─ Float
│ │ ├─ Math → .add()/.plus(), .subtract()/.sub()/.minus(), .multiply()/.mul()/.times(), .divide()/.div(), .remainder()/.mod()/.rem()/.modulo(), .pow(), .abs(), .sign(), .negate()
│ │ ├─ Advanced → .sqrt(), .exp(), .log(), .sin(), .cos(), .tan()
│ │ ├─ Convert → .toInteger()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne(), .lessThan()/.less()/.lt(), .greaterThan()/.greater()/.gt(), .lessThanOrEqual()/.lessEqual()/.lte()/.le(), .greaterThanOrEqual()/.greaterEqual()/.gte()/.ge()
│ ├─ String
│ │ ├─ Transform → .concat(), .repeat(), .substring(), .upperCase(), .lowerCase(), .trim(), .trimStart(), .trimEnd()
│ │ ├─ Replace → .replace(), .replaceAll(), .split()
│ │ ├─ Query → .length(), .startsWith(), .endsWith(), .contains(), .indexOf(), .charAt()
│ │ ├─ Parse → .parse(), .parseJson()
│ │ ├─ Encode → .encodeUtf8(), .encodeUtf16()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne(), .lessThan()/.less()/.lt(), .greaterThan()/.greater()/.gt(), .lessThanOrEqual()/.lessEqual()/.lte()/.le(), .greaterThanOrEqual()/.greaterEqual()/.gte()/.ge()
│ ├─ DateTime
│ │ ├─ Components → .getYear(), .getMonth(), .getDayOfMonth(), .getDayOfWeek(), .getHour(), .getMinute(), .getSecond(), .getMillisecond()
│ │ ├─ Arithmetic → .addDays(), .subtractDays(), .addHours(), .subtractHours(), .addMinutes(), .addSeconds(), .addMilliseconds(), .addWeeks()
│ │ ├─ Duration → .durationDays(), .durationHours(), .durationMinutes(), .durationSeconds(), .durationMilliseconds(), .durationWeeks()
│ │ ├─ Convert → .toEpochMilliseconds(), .printFormatted()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne(), .lessThan()/.less()/.lt(), .greaterThan()/.greater()/.gt(), .lessThanOrEqual()/.lessEqual()/.lte()/.le(), .greaterThanOrEqual()/.greaterEqual()/.gte()/.ge()
│ ├─ Blob
│ │ ├─ Read → .size(), .getUint8()
│ │ ├─ Decode → .decodeUtf8(), .decodeUtf16(), .decodeBeast(), .decodeCsv()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne()
│ ├─ Array
│ │ ├─ Read → .size(), .length(), .has(), .get(), .at(), .tryGet(), .getKeys()
│ │ ├─ Mutate → .update(), .pushLast(), .popLast(), .pushFirst(), .popFirst(), .append(), .prepend(), .clear(), .sortInPlace(), .reverseInPlace()
│ │ ├─ Transform → .copy(), .slice(), .concat(), .sort(), .reverse(), .map(), .filter(), .filterMap(), .flatMap()
│ │ ├─ Search → .findFirst(), .findAll(), .firstMap(), .isSorted()
│ │ ├─ Reduce → .reduce(), .every(), .some(), .sum(), .mean(), .maximum(), .minimum(), .findMaximum(), .findMinimum()
│ │ ├─ Convert → .stringJoin(), .toSet(), .toDict(), .flattenToSet(), .flattenToDict(), .encodeCsv()
│ │ ├─ Group → .groupReduce(), .groupSize(), .groupSum(), .groupMean(), .groupMinimum(), .groupMaximum(), .groupToArrays(), .groupToSets(), .groupToDicts(), .groupEvery(), .groupSome()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne()
│ ├─ Set
│ │ ├─ Read → .size(), .has()
│ │ ├─ Mutate → .insert(), .tryInsert(), .delete(), .tryDelete(), .clear(), .unionInPlace()
│ │ ├─ Set Ops → .copy(), .union(), .intersection(), .difference(), .symmetricDifference(), .isSubsetOf(), .isSupersetOf(), .isDisjointFrom()
│ │ ├─ Transform → .filter(), .filterMap(), .map(), .forEach(), .firstMap()
│ │ ├─ Reduce → .reduce(), .every(), .some(), .sum(), .mean()
│ │ ├─ Convert → .toArray(), .toSet(), .toDict(), .flattenToArray(), .flattenToSet(), .flattenToDict()
│ │ ├─ Group → .groupReduce(), .groupSize(), .groupSum(), .groupMean(), .groupToArrays(), .groupToSets(), .groupToDicts(), .groupEvery(), .groupSome()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne()
│ ├─ Dict
│ │ ├─ Read → .size(), .has(), .get(), .tryGet(), .keys(), .getKeys()
│ │ ├─ Mutate → .insert(), .insertOrUpdate(), .update(), .merge(), .getOrInsert(), .delete(), .tryDelete(), .pop(), .swap(), .clear(), .unionInPlace()
│ │ ├─ Transform → .copy(), .map(), .filter(), .filterMap(), .forEach(), .firstMap()
│ │ ├─ Reduce → .reduce(), .every(), .some(), .sum(), .mean()
│ │ ├─ Convert → .toArray(), .toSet(), .toDict(), .flattenToArray(), .flattenToSet(), .flattenToDict()
│ │ ├─ Group → .groupReduce(), .groupSize(), .groupSum(), .groupMean(), .groupToArrays(), .groupToSets(), .groupToDicts(), .groupEvery(), .groupSome()
│ │ └─ Compare → .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne()
│ ├─ Struct → .fieldName (direct property access)
│ ├─ Variant → .match(), .unwrap(), .hasTag(), .getTag(), .equals()/.equal()/.eq(), .notEquals()/.notEqual()/.ne()
│ └─ Ref → .get(), .update(), .merge()
│
├─ Standard Library (East.*)
│ ├─ Integer → East.Integer.printCommaSeperated(), .roundNearest(), .printOrdinal()
│ ├─ Float → East.Float.roundToDecimals(), .printCurrency(), .printCompact()
│ ├─ DateTime → East.DateTime.fromComponents(), .roundDownDay(), .parseFormatted()
│ ├─ Array → East.Array.range(), .linspace(), .generate()
│ ├─ Set → East.Set.generate()
│ ├─ Dict → East.Dict.generate()
│ ├─ Blob → East.Blob.encodeBeast(), blob.decodeCsv(), array.encodeCsv()
│ └─ String → East.String.printJson(), East.String.printError()
│
├─ Comparisons (East.*)
│ ├─ Equality → East.equal()/.equals()/.eq(), East.notEqual()/.notEquals()/.ne()
│ ├─ Ordering → East.less()/.lessThan()/.lt(), East.greater()/.greaterThan()/.gt()
│ ├─ Bounds → East.lessEqual()/.lte()/.le(), East.greaterEqual()/.gte()/.ge()
│ ├─ Identity → East.is() (reference equality for mutable types)
│ └─ Utilities → East.min(), East.max(), East.clamp()
│
├─ Conversion (East.*)
│ └─ To string → East.print(expr)
│
├─ Patch Operations (East.*)
│ ├─ Compute diff → East.diff(before, after) → returns patch
│ ├─ Apply patch → East.applyPatch(value, patch) → returns patched value
│ ├─ Compose patches → East.composePatch(first, second, type) → returns combined patch
│ └─ Invert patch → East.invertPatch(patch, type) → returns undo patch
│
└─ Serialization
├─ IR → fn.toIR(), ir.toJSON(), EastIR.fromJSON(data).compile(platform)
└─ Data → East.Blob.encodeBeast(value, 'v2'), blob.decodeBeast(type, 'v2')
Type System Summary
| Type | ValueTypeOf<Type> | Mutability |
|---|
NullType | null | Immutable |
BooleanType | boolean | Immutable |
IntegerType | bigint | Immutable |
FloatType | number | Immutable |
StringType | string | Immutable |
DateTimeType | Date | Immutable |
BlobType | Uint8Array | Immutable |
ArrayType<T> | ValueTypeOf<T>[] | Mutable |
SetType<K> | Set<ValueTypeOf<K>> | Mutable |
DictType<K, V> | Map<ValueTypeOf<K>, ValueTypeOf<V>> | Mutable |
RefType<T> | ref<ValueTypeOf<T>> | Mutable |
StructType<Fields> | {...} | Immutable |
VariantType<Cases> | variant | Immutable |
FunctionType<I, O> | Function | Immutable |
PatchType<T> | variant | Immutable |
Key Patterns
TypeScript Values vs East Expressions
TypeScript values and East expressions are different. East methods only work on East expressions.
typescript
1// WRONG - Cannot call East methods on TypeScript values
2const THRESHOLD = 100n; // This is a TypeScript bigint
3East.function([IntegerType], BooleanType, ($, x) => {
4 $.return(x.greaterThan(THRESHOLD)); // ERROR: THRESHOLD has no .greaterThan()
5});
6
7// CORRECT - Wrap TypeScript values with East.value()
8const THRESHOLD = 100n;
9East.function([IntegerType], BooleanType, ($, x) => {
10 $.return(x.greaterThan(East.value(THRESHOLD))); // OK
11});
12
13// ALSO CORRECT - Use $.const() inside the function
14East.function([IntegerType], BooleanType, ($, x) => {
15 const threshold = $.const(THRESHOLD); // Creates East expression
16 $.return(x.greaterThan(threshold)); // OK
17});
Rule: Function parameters are already East expressions. External TypeScript values must be wrapped with East.value() or bound with $.const().
Creating Variant Values
typescript
1import { variant, some, none, ref } from "@elaraai/east";
2
3// Option types - use some() and none helpers (preferred)
4const hasValue = some(42n); // { type: "some", value: 42n }
5const noValue = none; // { type: "none", value: null }
6
7// AVOID: variant("some", ...) and variant("none", ...) for Option types
8// const hasValue = variant("some", 42n); // works but use some() instead
9// const noValue = variant("none", null); // works but use none instead
10
11// Other variant types - use variant()
12const success = variant("ok", "done");
13const failure = variant("error", "failed");
14
15// Mutable references - use ref()
16const counter = ref(0n);
String Interpolation
typescript
1// East.str`` creates a StringExpr from a template
2// Only East expressions can be interpolated, NOT plain TypeScript values
3
4const MY_CONSTANT = 100n; // TypeScript value
5
6East.function([IntegerType], StringType, ($, x) => {
7 // CORRECT - x is already an East expression (function parameter)
8 const msg1 = $.let(East.str`Value: ${x}`);
9
10 // CORRECT - wrap TypeScript constant with East.value()
11 const msg2 = $.let(East.str`Threshold: ${East.value(MY_CONSTANT)}`);
12
13 // CORRECT - use $.const() to create East expression first
14 const threshold = $.const(MY_CONSTANT);
15 const msg3 = $.let(East.str`Threshold: ${threshold}`);
16
17 // WRONG - cannot interpolate plain TypeScript values
18 // const msg4 = $.let(East.str`Threshold: ${MY_CONSTANT}`); // ERROR
19
20 $.return(msg1);
21});
Error Handling
typescript
1$.try($ => {
2 $.assign(result, arr.get(index));
3}).catch(($, message, stack) => {
4 $.assign(result, -1n);
5}).finally($ => {
6 // cleanup
7});
typescript
1// Platform implementations are raw TypeScript functions that receive East values
2// (bigint for Integer, number for Float, string for String, etc.)
3
4// Sync platform - regular TypeScript function
5const log = East.platform("log", [StringType], NullType);
6const timeNs = East.platform("time_ns", [], IntegerType);
7
8// Async platform - async TypeScript function
9const fetchStatus = East.asyncPlatform("fetch_status", [StringType], StringType);
10
11// Implementations receive/return TypeScript values matching East types
12const platform = [
13 log.implement(console.log), // (msg: string) => void
14 timeNs.implement(() => process.hrtime.bigint()), // () => bigint
15 fetchStatus.implement(async (url: string) => { // async function OK
16 const response = await fetch(url);
17 return `${response.status}`;
18 }),
19];
20
21// In East code: NO await needed - async is handled automatically
22const myFn = East.asyncFunction([StringType], NullType, ($, url) => {
23 const t1 = $.let(timeNs());
24 const status = $.let(fetchStatus(url)); // no await, just call it
25 const t2 = $.let(timeNs());
26 $(log(East.str`Fetched in ${t2.subtract(t1)} ns, status: ${status}`));
27});
28
29// Compile async functions with East.compileAsync
30const compiled = East.compileAsync(myFn, platform);
31await compiled("https://example.com"); // await at the outer TypeScript level