66 KiB
East Developer Guide
Usage guide for the East programming language.
This guide covers core expression types and their operations. For additional formatting, conversion, and generation utilities, see the Standard Library documentation.
Table of Contents
Quick Start
East is a statically typed, expression-based language embedded in TypeScript. You write East programs using a fluent TypeScript API, then compile them to portable IR (Intermediate Representation) that can execute in different environments (javascript, julia, python, etc). East code runs in a controlled environment - you define platform functions that your East code can call, providing access to external capabilities like logging, database access, or any other effects you want to expose.
Workflow:
- Define platform functions - create an object with the external functions you want East code to access (e.g., logging, I/O, database queries)
- Define East functions using
East.function()with explicit types - Build expressions using methods on typed expression objects (
.add(),.map(), etc.) - Compile and run using
East.compile(fn, platform)to execute the code - (Optional) Serialize to IR using
.toIR()for transmission/storage across environments
Basic Example
import { East, IntegerType, ArrayType, StructType, StringType, DictType, NullType } from "@elaraai/east";
// Platform function for logging
const log = East.platform("log", [StringType], NullType);
const platform = [
log.implement(console.log),
];
// Define sale data type
const SaleType = StructType({
product: StringType,
quantity: IntegerType,
price: IntegerType
});
// Calculate revenue per product from sales data
const calculateRevenue = East.function(
[ArrayType(SaleType)],
DictType(StringType, IntegerType),
($, sales) => {
// Group sales by product and sum revenue (quantity × price)
const revenueByProduct = sales.groupSum(
// Group by product name
($, sale) => sale.product,
// Sum quantity × price
($, sale) => sale.quantity.multiply(sale.price)
);
// Log revenue for each product
$(log(East.str`Total Revenue: ${revenueByProduct.sum()}`));
$.return(revenueByProduct);
}
);
// Compile and execute
const compiled = East.compile(calculateRevenue, platform);
const sales = [
{ product: "Widget", quantity: 10n, price: 50n },
{ product: "Gadget", quantity: 5n, price: 100n },
{ product: "Widget", quantity: 3n, price: 50n }
];
const result = compiled(sales);
// Result: Map { "Widget" => 650n, "Gadget" => 500n }
// Logs: "Gadget: $500" and "Widget: $650"
Types
East is statically typed for speed and correctness, using structural typing for ease of use. All types (except functions) have a total ordering, enabling their use as Dict keys and Set elements, and allowing deep comparison operations.
Type System Concepts
EastType- A type descriptor (e.g.,IntegerType,StringType,ArrayType<IntegerType>)ValueTypeOf<T>- The JavaScript runtime value for a type (e.g.,bigintforIntegerType,stringforStringType)Expr<T>- A typed expression that can be composed and compiled (e.g.,IntegerExpr,StringExpr)
Key insight: Most East functions accept either Expr<T> OR ValueTypeOf<T>, allowing you to mix expressions and raw values.
| Type | JavaScript Value | Mutability | Description |
|---|---|---|---|
| Primitive Types | |||
NullType |
null |
Immutable | Unit type (single value) |
BooleanType |
boolean |
Immutable | True or false |
IntegerType |
bigint |
Immutable | 64-bit signed integers |
FloatType |
number |
Immutable | IEEE 754 double-precision (distinguishes -0.0 from 0.0) |
StringType |
string |
Immutable | UTF-8 text |
DateTimeType |
Date |
Immutable | UTC timestamp with millisecond precision |
BlobType |
Uint8Array |
Immutable | Binary data |
| Compound Types | |||
ArrayType<T> |
T[] |
Mutable | Ordered collection |
SetType<K> |
Set<K> |
Mutable | Sorted set (keys ordered by total ordering) |
DictType<K, V> |
Map<K, V> |
Mutable | Sorted dict (keys ordered by total ordering) |
StructType<Fields> |
{...} |
Immutable | Product type (field order matters) |
VariantType<Cases> |
variant |
Immutable | Sum type (cases sorted alphabetically) |
| Function Type | |||
FunctionType<I, O> |
Function | Immutable | First-class function (serializable as IR, not as data) |
Important Notes
- Total ordering: All types (even
FloatwithNaN,-0.0) have a defined total ordering - Immutable types: Can be used as Dict keys and Set elements
- Includes: All primitives, Blob, Struct, Variant
- Excludes: Array, Set, Dict, Function
- Data types: Can be serialized (excludes Function)
- Equality: Deep structural equality for all types
- Mutable types also support reference equality via
East.is()
- Mutable types also support reference equality via
- Field/case order:
- Struct field order is significant for structural typing
- Variant cases are automatically sorted alphabetically
- Operations marked ❗: Can throw runtime errors
East Namespace
The East namespace is the main entry point for building East programs using a fluent interface. In East, you construct programs by building expressions - typed values that can be composed, transformed, and eventually compiled to executable code.
Think of expressions as building blocks: East.value(42n) creates an integer expression, and you can call methods on it like .add(1n) to build larger expressions. The East namespace provides functions to create expressions from JavaScript values, perform comparisons, and access type-specific utilities.
Operations:
| Signature | Description | Example |
|---|---|---|
| Expression Creation | ||
value<V>(val: ValueTypeOf<V>): Expr<V> |
Create expression from JavaScript value | East.value(42n) |
value<T extends EastType>(val: Expr<T> | ValueTypeOf<T>, type: T): Expr<T> |
Create expression with explicit type | East.value(x, IntegerType) |
str`...`: StringExpr |
String interpolation template | East.str`Hello ${name}` |
print<T extends EastType>(expr: Expr<T>): StringExpr |
Convert any expression to string representation | East.print(x) |
| Function Definition | ||
function<I extends EastType[], O extends EastType>(inputs: I, output: O, body: ($, ...args) => Expr | value): FunctionExpr |
Define a function (see Function) | East.function([IntegerType], IntegerType, ($, x) => x.add(1n)) |
compile<I extends EastType[], O extends EastType>(fn: FunctionExpr<I, O>, platform: PlatformFunction[]): (...inputs) => ValueTypeOf<O> |
Compile East function to executable JavaScript | East.compile(myFunction, [log.implement(console.log)]) |
platform<I extends EastType[], O extends EastType>(name: string, inputs: I, output: O): (...args) => ExprType<O> |
Create callable helper for platform function | const log = East.platform("log", [StringType], NullType) |
| Comparisons | ||
equal<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Deep equality comparison | East.equal(x, 10n) |
notEqual<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Deep inequality comparison | East.notEqual(x, 0n) |
less<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Less than comparison (total ordering) | East.less(x, 100n) |
lessEqual<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Less than or equal comparison | East.lessEqual(x, y) |
greater<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Greater than comparison | East.greater(x, 0n) |
greaterEqual<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Greater than or equal comparison | East.greaterEqual(score, 50n) |
is<T extends DataType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): BooleanExpr |
Reference equality (for mutable types) | East.is(arr1, arr2) |
| Utilities | ||
min<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): Expr<T> |
Minimum of two values (uses total ordering) | East.min(x, 100n) |
max<T extends EastType>(a: Expr<T>, b: Expr<T> | ValueTypeOf<T>): Expr<T> |
Maximum of two values (uses total ordering) | East.max(x, 0n) |
clamp<T extends EastType>(x: Expr<T>, min: Expr<T> | ValueTypeOf<T>, max: Expr<T> | ValueTypeOf<T>): Expr<T> |
Clamp value between min and max | East.clamp(x, 0n, 100n) |
Functions
Functions in East are first-class values that can be defined, passed around, and called. They have concrete input and output types, and their bodies are written using a fluent interface.
This section follows the workflow from Quick Start:
- Define platform functions
- Define East functions
- Compile and execute
- (Optional) Serialize for transmission
Defining Platform Functions
East code runs in a sandboxed environment and can only interact with the outside world through platform functions that you explicitly provide. This ensures security and makes East code portable across different environments.
Creating platform functions:
Use East.platform() to create callable helpers that reference platform functions:
import { East, StringType, NullType, IntegerType } from "@elaraai/east";
// Define platform function helpers
const log = East.platform("log", [StringType], NullType);
// define the run-time implementation
const platform = [
log.implement(console.log),
];
Defining and compiling an East function:
const greet = East.function([StringType], NullType, ($, name) => {
// Call platform function from East code
$(log(East.str`Hello, ${name}!`));
$.return(null);
});
const compiled = East.compile(greet, platform);
compiled("Alice"); // Logs: "Hello, Alice!"
The compiled function has proper TypeScript types that match the East function signature.
Serializing an East function:
// Serialize to JSON
const ir = greet.toIR();
const jsonString = JSON.stringify(ir.toJSON());
// ... Send jsonString over network ...
Dynamically compile and execute the function on the remote environment
import { EastIR } from "@elaraai/east";
// define the remote environment run-time implementation
const remote_platform = {
log: (msg: string) => {
console.log(`Result: ${msg}`)
}
};
// Deserialize and compile on remote environment
const receivedIR = EastIR.fromJSON(JSON.parse("... jsonString value ... "));
// comile with a platform implementation on the remote environment!
const remote_compiled = receivedIR.compile(platform);
compiled("Bob"); // Logs: "Result: Hello, Bob!"
Operations:
The first argument in an east function body ($) is a BlockBuilder, which is an entry point to scope specific operations.
| Signature | Description | Example |
|---|---|---|
| Variables | ||
const<V>(value: ValueTypeOf<V>): Expr<V> |
Declare immutable variable (infers type) | const x = $.const(42n) |
const<T extends EastType>(value: Expr<T> | ValueTypeOf<T>, type: T): Expr<T> |
Declare immutable variable with explicit type | const x = $.const(y, IntegerType) |
let<V>(value: ValueTypeOf<V>): Expr<V> |
Declare mutable variable (infers type) | const x = $.let(0n) |
let<T extends EastType>(value: Expr<T> | ValueTypeOf<T>, type: T): Expr<T> |
Declare mutable variable with explicit type | const x = $.let(y, IntegerType) |
assign<T extends EastType>(variable: Expr<T>, value: Expr<T> | ValueTypeOf<T>): NullExpr |
Reassign mutable variable (must be declared with $.let) |
$.assign(x, 10n) |
| Execution | ||
$<T extends EastType>(expr: Expr<T>): Expr<T> |
Execute expression (often for side effects), returns the expression | $(arr.pushLast(42n)) |
return<Ret>(value: Expr<Ret> | ValueTypeOf<Ret>): NeverExpr |
Early return from function | $.return(x) |
error(message: StringExpr | string, location?: Location): NeverExpr |
Throw error with message | $.error("Invalid input") |
| Control Flow | ||
if(condition: BooleanExpr | boolean, body: ($) => void | Expr): IfBuilder |
If statement (chain with .elseIf(), .else()) |
$.if(East.greater(x, 0n), $ => $.return(x)) |
while(condition: BooleanExpr | boolean, body: ($, label) => void | Expr): NullExpr |
While loop | $.while(East.greater(x, 0n), ($, label) => $.assign(x, x.subtract(1n))) |
for<T extends EastType>(array: ArrayExpr<T>, body: ($, value, index, label) => void): NullExpr |
For loop over array elements | $.for(arr, ($, val, i, label) => $(total.add(val))) |
for<K extends DataType>(set: SetExpr<K>, body: ($, key, label) => void): NullExpr |
For loop over set keys | $.for(s, ($, key, label) => $(arr.pushLast(key))) |
for<K extends DataType, V extends EastType>(dict: DictExpr<K, V>, body: ($, value, key, label) => void): NullExpr |
For loop over dict entries | $.for(d, ($, val, key, label) => $(total.add(val))) |
break(label: Label): NeverExpr |
Break from loop (use label from loop body) | $.break(label) |
continue(label: Label): NeverExpr |
Continue to next iteration (use label from loop body) | $.continue(label) |
match<Cases>(variant: VariantExpr<Cases>, cases: { [K]: ($, data) => void | Expr }): NullExpr |
Pattern match on variant (statement form) | $.match(opt, { Some: ($, x) => $.return(x), None: $ => $.return(0n) }) |
try(body: ($) => void | Expr): TryCatchBuilder |
Try block (chain with .catch(($, message, stack) => ...)) |
$.try($ => arr.get(i)).catch(($, msg, stack) => $.return(0n)) |
Expressions
This section describes the operations available on each type's expressions.
Boolean
Boolean expressions support logical operations and conditional branching using the ternary-like ifElse method.
Example:
import { East, IntegerType, BooleanType } from "@elaraai/east";
const validateOrder = East.function([IntegerType, IntegerType, BooleanType], BooleanType, ($, quantity, price, isPremium) => {
// Create mutable boolean - starts as true, can be updated
const isValid = $.let(true);
// Alternative ways to create boolean values:
// const isValid = $.let(true, BooleanType);
// const isValid = $.let(East.value(true));
// const isValid = $.let(East.value(true, BooleanType));
// Check if order is too expensive without approval
$.if(East.greater(price, 10000n), $ => {
$.assign(isValid, false);
});
// Check for invalid quantity or price
$.if(East.less(quantity, 1n).or($ => East.less(price, 0n)), $ => {
$.assign(isValid, false);
});
// Combine checks: valid AND (premium OR large order)
const finalCheck = isValid.and($ => isPremium.or($ => East.greater(quantity, 100n)));
$.return(finalCheck);
});
const compiled = East.compile(validateOrder, []);
console.log(compiled(150n, 500n, true)); // true
console.log(compiled(50n, 500n, false)); // false
console.log(compiled(0n, 50n, false)); // false (invalid quantity)
Operations:
| Signature | Description | Example |
|---|---|---|
| Short-Circuiting Operations | ||
not(): BooleanExpr |
Logical NOT | x.not() |
and(y: ($) => BooleanExpr | boolean): BooleanExpr |
Logical AND (short-circuit) | x.and($ => y) |
or(y: ($) => BooleanExpr | boolean): BooleanExpr |
Logical OR (short-circuit) | x.or($ => y) |
ifElse(thenFn: ($) => any, elseFn: ($) => any): ExprType<TypeUnion<...>> |
Conditional expression (ternary) | condition.ifElse($ => trueValue, $ => falseValue) |
| Non-Short-Circuiting Operations | ||
bitAnd(y: BooleanExpr | boolean): BooleanExpr |
Bitwise AND (always evaluates both) | x.bitAnd(y) |
bitOr(y: BooleanExpr | boolean): BooleanExpr |
Bitwise OR (always evaluates both) | x.bitOr(y) |
bitXor(y: BooleanExpr | boolean): BooleanExpr |
Bitwise XOR (always evaluates both) | x.bitXor(y) |
Integer
Integer expressions (IntegerExpr) represent 64-bit signed integers with standard arithmetic, mathematical functions, and rich formatting utilities in the standard library.
Example:
import { East, IntegerType, StringType } from "@elaraai/east";
const formatRevenue = East.function([IntegerType], StringType, ($, revenue) => {
// Create integer value with $.let() and East.value() (type inference)
const price = $.let(East.value(47n));
const rounded = price.add(5n).divide(10n).multiply(10n); // Round to nearest 10
// Alternative: Create integer value with $.let() and East.value() with explicit type
const bonus = $.let(East.value(1000n, IntegerType));
const quarterly = revenue.add(bonus).divide(12n).multiply(3n);
$.return(East.str`Revenue: ${revenue}, Price: ${rounded}, Quarterly: ${quarterly}`);
});
const compiled = East.compile(formatRevenue, []);
console.log(compiled(1234567n)); // "Revenue: 1234567, Price: 50, Quarterly: 308641"
Operations:
| Signature | Description | Example |
|---|---|---|
negate(): IntegerExpr |
Unary negation | x.negate() |
add(y: IntegerExpr | bigint): IntegerExpr |
Addition | x.add(5n) |
subtract(y: IntegerExpr | bigint): IntegerExpr |
Subtraction | x.subtract(3n) |
multiply(y: IntegerExpr | bigint): IntegerExpr |
Multiplication | x.multiply(2n) |
divide(y: IntegerExpr | bigint): IntegerExpr |
Integer division (floored), 0 / 0 = 0 |
x.divide(10n) |
remainder(y: IntegerExpr | bigint): IntegerExpr |
Remainder (floored modulo) | x.remainder(3n) |
pow(y: IntegerExpr | bigint): IntegerExpr |
Exponentiation | x.pow(2n) |
abs(): IntegerExpr |
Absolute value | x.abs() |
sign(): IntegerExpr |
Sign (-1, 0, or 1) | x.sign() |
log(base: IntegerExpr | bigint): IntegerExpr |
Logarithm (floored, custom base) | x.log(10n) |
toFloat(): FloatExpr |
Convert to float (may be approximate) | x.toFloat() |
Standard Library: See STDLIB.md for additional formatting and rounding functions.
Float
Float expressions (FloatExpr) represent IEEE 754 double-precision floating-point numbers with standard arithmetic and mathematical functions.
Example:
const calculateCircle = East.function([FloatType], FloatType, ($, radius) => {
// Create float value with $.let() and East.value() (type inference)
const pi = $.let(East.value(3.14159));
const area = pi.multiply(radius.pow(2.0));
// Alternative: Create float value with $.let() and East.value() with explicit type
const scaleFactor = $.let(East.value(1.5, FloatType));
const scaled = area.multiply(scaleFactor);
$.return(scaled);
});
const compiled = East.compile(calculateCircle, []);
console.log(compiled(10.0)); // 471.2385
Operations:
| Signature | Description | Example |
|---|---|---|
negate(): FloatExpr |
Unary negation | x.negate() |
add(y: FloatExpr | number): FloatExpr |
Addition | x.add(2.5) |
subtract(y: FloatExpr | number): FloatExpr |
Subtraction | x.subtract(1.5) |
multiply(y: FloatExpr | number): FloatExpr |
Multiplication | x.multiply(2.0) |
divide(y: FloatExpr | number): FloatExpr |
Division, 0.0 / 0.0 = NaN |
x.divide(2.0) |
remainder(y: FloatExpr | number): FloatExpr |
Remainder (floored modulo) | x.remainder(3.0) |
pow(y: FloatExpr | number): FloatExpr |
Exponentiation | x.pow(2.0) |
abs(): FloatExpr |
Absolute value | x.abs() |
sign(): FloatExpr |
Sign (-1, 0, or 1) | x.sign() |
sqrt(): FloatExpr |
Square root | x.sqrt() |
exp(): FloatExpr |
Exponential (e^x) | x.exp() |
log(): FloatExpr |
Natural logarithm | x.log() |
sin(): FloatExpr |
Sine | x.sin() |
cos(): FloatExpr |
Cosine | x.cos() |
tan(): FloatExpr |
Tangent | x.tan() |
toInteger(): IntegerExpr ❗ |
Convert to integer (must be exact, errors otherwise) | x.toInteger() |
String
String expressions (StringExpr) provide text manipulation, pattern matching, encoding, and parsing capabilities.
Example:
const processEmail = East.function([StringType], StringType, ($, email) => {
const atIndex = email.indexOf("@");
const domain = email.substring(atIndex.add(1n), email.length());
// Create string value with $.let() and East.value() (type inference)
const greeting = $.let(East.value("Hello"));
// Alternative: Create string value with $.let() and East.value() with explicit type
const separator = $.let(East.value(" ", StringType));
const message = greeting.concat(separator).concat(domain);
$.return(message.upperCase());
});
const compiled = East.compile(processEmail, []);
console.log(compiled("user@example.com")); // "HELLO EXAMPLE.COM"
Operations:
| Signature | Description | Example |
|---|---|---|
| Manipulation | ||
concat(other: StringExpr | string): StringExpr |
Concatenate strings | str.concat(" world") |
repeat(count: IntegerExpr | bigint): StringExpr |
Repeat string n times | str.repeat(3n) |
substring(from: IntegerExpr | bigint, to: IntegerExpr | bigint): StringExpr |
Extract substring | str.substring(0n, 5n) |
upperCase(): StringExpr |
Convert to uppercase | str.upperCase() |
lowerCase(): StringExpr |
Convert to lowercase | str.lowerCase() |
trim(): StringExpr |
Remove whitespace from both ends | str.trim() |
trimStart(): StringExpr |
Remove whitespace from start | str.trimStart() |
trimEnd(): StringExpr |
Remove whitespace from end | str.trimEnd() |
split(separator: StringExpr | string): ArrayExpr<StringType> |
Split into array | str.split(",") |
replace(search: StringExpr | string, replacement: StringExpr | string): StringExpr |
Replace first occurrence | str.replace("old", "new") |
| Query | ||
length(): IntegerExpr |
Get string length (UTF-16 code units) | str.length() |
startsWith(prefix: StringExpr | string): BooleanExpr |
Test if starts with prefix | str.startsWith("Hello") |
endsWith(suffix: StringExpr | string): BooleanExpr |
Test if ends with suffix | str.endsWith(".txt") |
contains(substring: StringExpr | string): BooleanExpr |
Test if contains substring | str.contains("world") |
contains(regex: RegExp): BooleanExpr |
Test if matches regex | str.contains(/[0-9]+/) |
indexOf(substring: StringExpr | string): IntegerExpr |
Find index of substring (-1 if not found) | str.indexOf("world") |
indexOf(regex: RegExp): IntegerExpr |
Find index of regex match (-1 if not found) | str.indexOf(/[0-9]+/) |
| Encoding | ||
encodeUtf8(): BlobExpr |
Encode as UTF-8 bytes | str.encodeUtf8() |
encodeUtf16(): BlobExpr |
Encode as UTF-16 bytes (little-endian with BOM) | str.encodeUtf16() |
| Parsing | ||
parse<T extends DataType>(type: T): ExprType<T> ❗ |
Parse string to given type (fallible) | str.parse(IntegerType) |
parseJson<T extends DataType>(type: T): ExprType<T> ❗ |
Parse JSON to given type (fallible) | str.parseJson(IntegerType) |
Standard Library: See STDLIB.md for error formatting utilities.
DateTime
DateTime expressions (DateTimeExpr) represent UTC timestamps with millisecond precision, supporting component access, arithmetic, and rounding operations.
Example:
const addBusinessDays = East.function([DateTimeType, IntegerType], DateTimeType, ($, startDate, days) => {
// Create datetime value with $.let() and East.value() (type inference)
const baseDate = $.let(East.value(new Date("2025-01-01T00:00:00Z")));
// Alternative: Create datetime value with $.let() and East.value() with explicit type
const epoch = $.let(East.value(new Date("1970-01-01T00:00:00Z"), DateTimeType));
const result = startDate.addDays(days);
const rounded = East.DateTime.roundDownDay(result, 1n);
$.return(rounded);
});
const compiled = East.compile(addBusinessDays, []);
const start = new Date("2025-10-10T14:30:00Z");
const end = compiled(start, 7n);
console.log(end); // 2025-10-17 00:00:00 UTC
Operations:
| Signature | Description | Example |
|---|---|---|
| Component Access | ||
getYear(): IntegerExpr |
Get year | date.getYear() |
getMonth(): IntegerExpr |
Get month (1-12) | date.getMonth() |
getDayOfMonth(): IntegerExpr |
Get day of month (1-31) | date.getDayOfMonth() |
getDayOfWeek(): IntegerExpr |
Get day of week (0-6, Sunday=0) | date.getDayOfWeek() |
getHour(): IntegerExpr |
Get hour (0-23) | date.getHour() |
getMinute(): IntegerExpr |
Get minute (0-59) | date.getMinute() |
getSecond(): IntegerExpr |
Get second (0-59) | date.getSecond() |
getMillisecond(): IntegerExpr |
Get millisecond (0-999) | date.getMillisecond() |
| Arithmetic | ||
addMilliseconds(ms: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Add milliseconds (int or float) | date.addMilliseconds(1000n) |
subtractMilliseconds(ms: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Subtract milliseconds | date.subtractMilliseconds(500n) |
addSeconds(s: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Add seconds | date.addSeconds(60n) |
subtractSeconds(s: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Subtract seconds | date.subtractSeconds(30n) |
addMinutes(m: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Add minutes | date.addMinutes(10n) |
subtractMinutes(m: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Subtract minutes | date.subtractMinutes(5n) |
addHours(h: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Add hours | date.addHours(2n) |
subtractHours(h: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Subtract hours | date.subtractHours(1n) |
addDays(d: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Add days | date.addDays(7n) |
subtractDays(d: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Subtract days | date.subtractDays(1n) |
addWeeks(w: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Add weeks | date.addWeeks(2n) |
subtractWeeks(w: IntegerExpr | FloatExpr | bigint | number): DateTimeExpr |
Subtract weeks | date.subtractWeeks(1n) |
| Duration | ||
durationMilliseconds(other: DateTimeExpr | Date): IntegerExpr |
Duration in milliseconds (positive if other > this) | date.durationMilliseconds(otherDate) |
durationSeconds(other: DateTimeExpr | Date): FloatExpr |
Duration in seconds | date.durationSeconds(otherDate) |
durationMinutes(other: DateTimeExpr | Date): FloatExpr |
Duration in minutes | date.durationMinutes(otherDate) |
durationHours(other: DateTimeExpr | Date): FloatExpr |
Duration in hours | date.durationHours(otherDate) |
durationDays(other: DateTimeExpr | Date): FloatExpr |
Duration in days | date.durationDays(otherDate) |
durationWeeks(other: DateTimeExpr | Date): FloatExpr |
Duration in weeks | date.durationWeeks(otherDate) |
| Conversion | ||
toEpochMilliseconds(): IntegerExpr |
Milliseconds since Unix epoch | date.toEpochMilliseconds() |
Standard Library: See STDLIB.md for construction from components and additional rounding functions.
Blob
Blob expressions (BlobExpr) represent immutable binary data with byte access and encoding/decoding operations.
Example:
const encodeData = East.function([IntegerType], BlobType, ($, value) => {
const encoded = East.Blob.encodeBeast(value, 'v2');
// Create blob value with $.let() and East.value() (type inference)
const header = $.let(East.value(new Uint8Array([0x42, 0x45])));
// Alternative: Create blob value with $.let() and East.value() with explicit type
const footer = $.let(East.value(new Uint8Array([0xFF]), BlobType));
$.return(encoded);
});
const compiled = East.compile(encodeData, []);
const blob = compiled(42n);
console.log(blob); // Uint8Array containing BEAST-encoded 42n
Operations:
| Signature | Description | Example |
|---|---|---|
| Base Operations | ||
size(): IntegerExpr |
Get size in bytes | blob.size() |
getUint8(offset: IntegerExpr | bigint): IntegerExpr ❗ |
Get byte at offset (0-255, errors if out of bounds) | blob.getUint8(0n) |
decodeUtf8(): StringExpr ❗ |
Decode as UTF-8 (fallible) | blob.decodeUtf8() |
decodeUtf16(): StringExpr ❗ |
Decode as UTF-16 (fallible) | blob.decodeUtf16() |
decodeBeast<T extends EastType>(type: T, version: 'v1' | 'v2' = 'v1'): ExprType<T> ❗ |
Decode binary BEAST format (v1 or v2, fallible) | blob.decodeBeast(IntegerType, 'v2') |
Standard Library: See STDLIB.md for BEAST encoding utilities.
Array
Array expressions (ArrayExpr<T>) represent mutable, ordered collections with rich functional operations, mutations, and conversions.
Example:
const processPrices = East.function([ArrayType(IntegerType)], IntegerType, ($, prices) => {
const doubled = prices.map(($, x, i) => x.multiply(2n));
const filtered = doubled.filter(($, x, i) => East.greater(x, 100n));
const sum = filtered.sum();
// Create array value with $.let() and East.value() (type inference)
const bonuses = $.let(East.value([10n, 20n, 30n]));
// Alternative: Create array value with $.let() and East.value() with explicit type
const fees = $.let(East.value([5n, 10n], ArrayType(IntegerType)));
$(prices.pushLast(999n)); // Mutate original array
$.return(sum);
});
const compiled = East.compile(processPrices, []);
console.log(compiled([50n, 60n, 70n])); // 260n (60*2 + 70*2)
Operations:
| Signature | Description | Example |
|---|---|---|
| Read Operations | ||
size(): IntegerExpr |
Get array length | array.size() |
has(index: IntegerExpr | bigint): BooleanExpr |
Check if index is valid (0 ≤ index < size) | array.has(5n) |
get<V extends EastType>(index: IntegerExpr | bigint): ExprType<V> ❗ |
Get element (errors if out of bounds) | array.get(0n) |
get<V extends EastType>(index: IntegerExpr | bigint, defaultFn: FunctionType<[IntegerType], V>): ExprType<V> |
Get element or compute default | array.get(10n, East.function([IntegerType], IntegerType, ($, i) => 0n)) |
tryGet<V extends EastType>(index: IntegerExpr | bigint): OptionExpr<V> |
Safe get returning Option | array.tryGet(0n) |
| Mutation Operations | ||
update<V extends EastType>(index: IntegerExpr | bigint, value: ExprType<V> | ValueTypeOf<V>): NullExpr ❗ |
Replace element (errors if out of bounds) | array.update(0n, 42n) |
merge<V extends EastType, T2 extends EastType>(index: IntegerExpr | bigint, value: Expr<T2>, updateFn: FunctionType<[V, T2, IntegerType], V>): ExprType<V> |
Merge value with existing element using function | array.merge(0n, 5n, ($, old, new, i) => old.add(new)) |
pushLast<V extends EastType>(value: ExprType<V> | ValueTypeOf<V>): NullExpr |
Append to end | array.pushLast(42n) |
popLast<V extends EastType>(): ExprType<V> ❗ |
Remove from end (errors if empty) | array.popLast() |
pushFirst<V extends EastType>(value: ExprType<V> | ValueTypeOf<V>): NullExpr |
Prepend to start | array.pushFirst(42n) |
popFirst<V extends EastType>(): ExprType<V> ❗ |
Remove from start (errors if empty) | array.popFirst() |
append<V extends EastType>(other: ArrayExpr<V>): NullExpr |
Append all elements from other array (mutating) | array.append(otherArray) |
prepend<V extends EastType>(other: ArrayExpr<V>): NullExpr |
Prepend all elements from other array (mutating) | array.prepend(otherArray) |
mergeAll<V extends EastType, T2 extends EastType>(other: ArrayExpr<T2>, mergeFn: FunctionType<[V, T2, IntegerType], V>): NullExpr |
Merge all elements from another array using function | array.mergeAll(other, ($, cur, new, i) => cur.add(new)) |
clear(): NullExpr |
Remove all elements | array.clear() |
sortInPlace<V extends EastType>(byFn?: FunctionType<[V], DataType>): NullExpr |
Sort in-place | array.sortInPlace() |
reverseInPlace(): NullExpr |
Reverse in-place | array.reverseInPlace() |
| Functional Operations (Immutable) | ||
copy<V extends EastType>(): ArrayExpr<V> |
Shallow copy | array.copy() |
slice<V extends EastType>(start: IntegerExpr | bigint, end: IntegerExpr | bigint): ArrayExpr<V> |
Extract subarray | array.slice(0n, 10n) |
concat<V extends EastType>(other: ArrayExpr<V>): ArrayExpr<V> |
Concatenate into new array | array.concat(otherArray) |
getKeys<V extends EastType>(keys: ArrayExpr<IntegerType>, onMissing?: FunctionType<[IntegerType], V>): ArrayExpr<V> |
Get values at given indices | array.getKeys(indices, East.function([IntegerType], IntegerType, ($, i) => 0n)) |
sort<V extends EastType>(byFn?: FunctionType<[V], DataType>): ArrayExpr<V> |
Return sorted copy | array.sort() |
reverse<V extends EastType>(): ArrayExpr<V> |
Return reversed copy | array.reverse() |
isSorted<V extends EastType>(byFn?: FunctionType<[V], DataType>): BooleanExpr |
Check if sorted | array.isSorted() |
findSortedFirst<V extends EastType, T2 extends EastType>(value: T2, byFn?: FunctionType<[V], TypeOf<T2>>): IntegerExpr |
Binary search for first element ≥ value | array.findSortedFirst(42n) |
findSortedLast<V extends EastType, T2 extends EastType>(value: T2, byFn?: FunctionType<[V], TypeOf<T2>>): IntegerExpr |
Binary search for last element ≤ value | array.findSortedLast(42n) |
findSortedRange<V extends EastType, T2 extends EastType>(value: T2, byFn?: FunctionType<[V], TypeOf<T2>>): StructExpr<{start, end}> |
Binary search for range of elements equal to value | array.findSortedRange(42n) |
map<V extends EastType, U extends EastType>(fn: FunctionType<[V, IntegerType], U>): ArrayExpr<U> |
Transform each element | array.map(($, x, i) => x.multiply(2n)) |
filter<V extends EastType>(predicate: FunctionType<[V, IntegerType], BooleanType>): ArrayExpr<V> |
Keep matching elements | array.filter(($, x, i) => East.greater(x, 0n)) |
filterMap<V extends EastType, U extends EastType>(fn: FunctionType<[V, IntegerType], OptionType<U>>): ArrayExpr<U> |
Filter and map using Option | array.filterMap(($, x, i) => East.greater(x, 0n) ? East.some(x.multiply(2n)) : East.none()) |
firstMap<V extends EastType, U extends EastType>(fn: FunctionType<[V, IntegerType], OptionType<U>>): OptionExpr<U> |
Map until first successful result (returns Option) | array.firstMap(($, x, i) => East.greater(x, 0n) ? East.some(x) : East.none()) |
findFirst<V extends EastType>(value: V): OptionExpr<IntegerType> |
Find index of first matching element | array.findFirst(42n) |
findFirst<V extends EastType, T2 extends EastType>(value: T2, by: FunctionType<[V, IntegerType], T2>): OptionExpr<IntegerType> |
Find first match with projection | array.findFirst("active", ($, u, i) => u.status) |
findAll<V extends EastType>(value: V): ArrayExpr<IntegerType> |
Find all indices of matching elements | array.findAll(42n) |
findAll<V extends EastType, T2 extends EastType>(value: T2, by: FunctionType<[V, IntegerType], T2>): ArrayExpr<IntegerType> |
Find all matches with projection | array.findAll("active", ($, u, i) => u.status) |
forEach<V extends EastType>(fn: FunctionType<[V, IntegerType], any>): NullExpr |
Execute function for each element | array.forEach(($, x, i) => $(total.add(x))) |
| Reduction Operations | ||
reduce<V extends EastType, T extends EastType>(combineFn: FunctionType<[T, V, IntegerType], T>, init: T): ExprType<T> |
Fold/reduce with initial value | array.reduce(($, acc, x, i) => acc.add(x), 0n) |
reduce<V extends EastType>(combineFn: FunctionType<[V, V, IntegerType], V>): ExprType<V> |
Fold/reduce without initial (uses first element) | array.reduce(($, acc, x, i) => acc.add(x)) |
mapReduce<V extends EastType, U extends EastType, T extends EastType>(mapFn: FunctionType<[V, IntegerType], U>, combineFn: FunctionType<[T, U, IntegerType], T>, init: T): ExprType<T> |
Map then reduce with initial value | array.mapReduce(($, x, i) => x.multiply(2n), ($, acc, x, i) => acc.add(x), 0n) |
mapReduce<V extends EastType, U extends EastType>(mapFn: FunctionType<[V, IntegerType], U>, combineFn: FunctionType<[U, U, IntegerType], U>): ExprType<U> |
Map then reduce without initial | array.mapReduce(($, x, i) => x.multiply(2n), ($, acc, x, i) => acc.add(x)) |
every<V extends EastType>(predicate?: FunctionType<[V, IntegerType], BooleanType>): BooleanExpr |
True if all match predicate | array.every() |
some<V extends EastType>(predicate?: FunctionType<[V, IntegerType], BooleanType>): BooleanExpr |
True if any match predicate | array.some() |
sum<V extends IntegerType | FloatType>(): IntegerExpr | FloatExpr |
Sum of numeric array | array.sum() |
sum<V extends EastType>(fn: FunctionType<[V, IntegerType], IntegerType | FloatType>): IntegerExpr | FloatExpr |
Sum with projection | array.sum(($, x, i) => x.multiply(2n)) |
mean<V extends IntegerType | FloatType>(): FloatExpr |
Mean (NaN if empty) | array.mean() |
mean<V extends EastType>(fn: FunctionType<[V, IntegerType], IntegerType | FloatType>): FloatExpr |
Mean with projection | array.mean(($, x, i) => x.toFloat()) |
findMaximum<V extends EastType>(by?: FunctionType<[V, IntegerType], any>): OptionExpr<IntegerType> |
Find index of maximum element | array.findMaximum() |
findMinimum<V extends EastType>(by?: FunctionType<[V, IntegerType], any>): OptionExpr<IntegerType> |
Find index of minimum element | array.findMinimum() |
maximum<V extends EastType>(by?: FunctionType<[V, IntegerType], any>): ExprType<V> |
Get maximum element value (errors if empty) | array.maximum() |
minimum<V extends EastType>(by?: FunctionType<[V, IntegerType], any>): ExprType<V> |
Get minimum element value (errors if empty) | array.minimum() |
| Conversion Operations | ||
stringJoin<V extends StringType>(separator: StringExpr | string): StringExpr |
Join string array (only for ArrayType<StringType>) |
array.stringJoin(", ") |
toSet<V extends EastType, K extends DataType>(keyFn?: FunctionType<[V, IntegerType], K>): SetExpr<K> |
Convert to set (ignoring duplicates) | array.toSet() |
toDict<V extends EastType, K extends DataType, U extends EastType>(keyFn?: FunctionType<[V, IntegerType], K>, valueFn?: FunctionType<[V, IntegerType], U>, onConflictFn?: FunctionType<[U, U, K], U>): DictExpr<K, U> |
Convert to dict | array.toDict(($, x, i) => i) |
flatMap<V extends EastType, U extends EastType>(fn?: FunctionType<[V, IntegerType], ArrayType<U>>): ArrayExpr<U> |
Flatten array of arrays | array.flatMap() |
flattenToSet<V extends EastType, K extends DataType>(fn?: FunctionType<[V, IntegerType], SetType<K>>): SetExpr<K> |
Flatten to set | array.flattenToSet() |
flattenToDict<V extends EastType, K extends DataType, U extends EastType>(fn?: FunctionType<[V, IntegerType], DictType<K, U>>, onConflictFn?: FunctionType<[U, U, K], U>): DictExpr<K, U> |
Flatten to dict | array.flattenToDict() |
| Grouping Operations | ||
groupReduce<V extends EastType, K extends DataType, U extends EastType, T extends EastType>(keyFn: FunctionType<[V, IntegerType], K>, valueFn: FunctionType<[V, IntegerType], U>, initFn: FunctionType<[K], T>, reduceFn: FunctionType<[T, U, K], T>): DictExpr<K, T> |
Group by key and reduce groups | array.groupReduce(($, x, i) => x.remainder(2n), ($, x, i) => x, ($, key) => 0n, ($, acc, val, key) => acc.add(val)) |
groupSize<V extends EastType, K extends DataType>(keyFn?: FunctionType<[V, IntegerType], K>): DictExpr<K, IntegerType> |
Count elements in each group | array.groupSize(($, x, i) => x.remainder(2n)) |
groupEvery<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, predFn: FunctionType<[V, IntegerType], BooleanType>): DictExpr<K, BooleanType> |
Check if all elements in each group match predicate | array.groupEvery(($, x, i) => x.remainder(2n), ($, x, i) => East.greater(x, 0n)) |
groupSome<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, predFn: FunctionType<[V, IntegerType], BooleanType>): DictExpr<K, BooleanType> |
Check if any element in each group matches predicate | array.groupSome(($, x, i) => x.remainder(2n), ($, x, i) => East.greater(x, 10n)) |
groupFindFirst<V extends EastType, K extends DataType, T2 extends EastType>(keyFn: FunctionType<[V, IntegerType], K>, value: T2, projFn?: FunctionType<[V, IntegerType], T2>): DictExpr<K, OptionType<IntegerType>> |
Find first matching index in each group | array.groupFindFirst(($, x, i) => x.remainder(2n), 42n) |
groupFindAll<V extends EastType, K extends DataType, T2 extends EastType>(keyFn: FunctionType<[V, IntegerType], K>, value: T2, projFn?: FunctionType<[V, IntegerType], T2>): DictExpr<K, ArrayType<IntegerType>> |
Find all matching indices in each group | array.groupFindAll(($, x, i) => x.remainder(2n), 42n) |
groupFindMinimum<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, byFn?: FunctionType<[V, IntegerType], any>): DictExpr<K, IntegerType> |
Find index of minimum in each group | array.groupFindMinimum(($, x, i) => x.remainder(2n)) |
groupFindMaximum<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, byFn?: FunctionType<[V, IntegerType], any>): DictExpr<K, IntegerType> |
Find index of maximum in each group | array.groupFindMaximum(($, x, i) => x.remainder(2n)) |
groupSum<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, valueFn?: FunctionType<[V, IntegerType], IntegerType | FloatType>): DictExpr<K, IntegerType | FloatType> |
Sum values in each group | array.groupSum(($, x, i) => x.remainder(2n)) |
groupMean<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, valueFn?: FunctionType<[V, IntegerType], IntegerType | FloatType>): DictExpr<K, FloatType> |
Mean of values in each group | array.groupMean(($, x, i) => x.remainder(2n)) |
groupMinimum<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, byFn?: FunctionType<[V, IntegerType], any>): DictExpr<K, V> |
Get minimum value in each group | array.groupMinimum(($, x, i) => x.remainder(2n)) |
groupMaximum<V extends EastType, K extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, byFn?: FunctionType<[V, IntegerType], any>): DictExpr<K, V> |
Get maximum value in each group | array.groupMaximum(($, x, i) => x.remainder(2n)) |
groupToArrays<V extends EastType, K extends DataType, U extends EastType>(keyFn: FunctionType<[V, IntegerType], K>, valueFn?: FunctionType<[V, IntegerType], U>): DictExpr<K, ArrayType<U>> |
Collect elements into arrays by group | array.groupToArrays(($, x, i) => x.remainder(2n)) |
groupToSets<V extends EastType, K extends DataType, U extends DataType>(keyFn: FunctionType<[V, IntegerType], K>, valueFn?: FunctionType<[V, IntegerType], U>): DictExpr<K, SetType<U>> |
Collect elements into sets by group | array.groupToSets(($, x, i) => x.remainder(2n)) |
groupToDicts<V extends EastType, K extends DataType, K2 extends DataType, U extends EastType>(keyFn: FunctionType<[V, IntegerType], K>, keyFn2: FunctionType<[V, IntegerType], K2>, valueFn?: FunctionType<[V, IntegerType], U>, combineFn?: FunctionType<[U, U, K2], U>): DictExpr<K, DictType<K2, U>> |
Collect elements into nested dicts | array.groupToDicts(($, x, i) => x.remainder(2n), ($, x, i) => i, ($, x, i) => x) |
Standard Library: See STDLIB.md for array generation functions (range, linspace, generate).
Set
Set expressions (SetExpr<K>) represent mutable, sorted collections of unique keys with set operations and functional transformations.
Example:
const processSet = East.function([SetType(IntegerType), SetType(IntegerType)], IntegerType, ($, setA, setB) => {
const unionSet = setA.union(setB);
const intersection = setA.intersection(setB);
// Create set value with $.let() and East.value() (type inference)
const extras = $.let(East.value(new Set([100n, 200n])));
// Alternative: Create set value with $.let() and East.value() with explicit type
const defaults = $.let(East.value(new Set([1n, 2n]), SetType(IntegerType)));
$(setA.insert(999n)); // Mutate original set
const total = unionSet.sum();
$.return(total);
});
const compiled = East.compile(processSet, []);
const a = new Set([1n, 2n, 3n]);
const b = new Set([3n, 4n, 5n]);
console.log(compiled(a, b)); // 15n (1+2+3+4+5)
Operations:
| Signature | Description | Example |
|---|---|---|
| Read Operations | ||
size(): IntegerExpr |
Get set size | set.size() |
has<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): BooleanExpr |
Check if key exists | set.has(42n) |
| Mutation Operations | ||
insert<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): NullExpr ❗ |
Insert key (errors if exists) | set.insert(42n) |
tryInsert<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): BooleanExpr |
Safe insert (returns success) | set.tryInsert(42n) |
delete<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): NullExpr ❗ |
Delete key (errors if missing) | set.delete(42n) |
tryDelete<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): BooleanExpr |
Safe delete (returns success) | set.tryDelete(42n) |
clear(): NullExpr |
Remove all elements | set.clear() |
unionInPlace<K extends DataType>(other: SetExpr<K>): NullExpr |
Union in-place (mutating) | set.unionInPlace(otherSet) |
| Set Operations | ||
copy<K extends DataType>(): SetExpr<K> |
Shallow copy | set.copy() |
union<K extends DataType>(other: SetExpr<K>): SetExpr<K> |
Return union | set.union(otherSet) |
intersection<K extends DataType>(other: SetExpr<K>): SetExpr<K> |
Return intersection | set.intersection(otherSet) |
difference<K extends DataType>(other: SetExpr<K>): SetExpr<K> |
Return difference (in this, not in other) | set.difference(otherSet) |
symmetricDifference<K extends DataType>(other: SetExpr<K>): SetExpr<K> |
Return symmetric difference | set.symmetricDifference(otherSet) |
isSubsetOf<K extends DataType>(other: SetExpr<K>): BooleanExpr |
Check if subset | set.isSubsetOf(otherSet) |
isSupersetOf<K extends DataType>(other: SetExpr<K>): BooleanExpr |
Check if superset | set.isSupersetOf(otherSet) |
isDisjointFrom<K extends DataType>(other: SetExpr<K>): BooleanExpr |
Check if no common elements | set.isDisjointFrom(otherSet) |
| Functional Operations (Immutable) | ||
filter<K extends DataType>(predicate: FunctionType<[K], BooleanType>): SetExpr<K> |
Keep matching elements | set.filter(($, key) => East.greater(key, 0n)) |
filterMap<K extends DataType, V extends EastType>(fn: FunctionType<[K], OptionType<V>>): ArrayExpr<V> |
Filter and map using Option | set.filterMap(($, key) => East.greater(key, 0n) ? East.some(key) : East.none()) |
firstMap<K extends DataType, V extends EastType>(fn: FunctionType<[K], OptionType<V>>): OptionExpr<V> |
Map until first successful result | set.firstMap(($, key) => East.greater(key, 10n) ? East.some(key) : East.none()) |
forEach<K extends DataType>(fn: FunctionType<[K], any>): NullExpr |
Execute function for each element | set.forEach(($, key) => $(arr.pushLast(key))) |
map<K extends DataType, V extends EastType>(fn: FunctionType<[K], V>): DictExpr<K, V> |
Map to dict (keys unchanged, values from fn) | set.map(($, key) => key.multiply(2n)) |
reduce<K extends DataType, T extends EastType>(fn: FunctionType<[T, K], T>, init: T): ExprType<T> |
Fold/reduce over set | set.reduce(($, acc, key) => acc.add(key), 0n) |
every<K extends DataType>(fn?: FunctionType<[K], BooleanType>): BooleanExpr |
True if all match | set.every() |
some<K extends DataType>(fn?: FunctionType<[K], BooleanType>): BooleanExpr |
True if any match | set.some() |
sum<K extends IntegerType | FloatType>(): IntegerExpr | FloatExpr |
Sum of numeric set | set.sum() |
sum<K extends DataType>(fn: FunctionType<[K], IntegerType | FloatType>): IntegerExpr | FloatExpr |
Sum with projection | set.sum(($, key) => key.multiply(2n)) |
mean<K extends IntegerType | FloatType>(): FloatExpr |
Mean (NaN if empty) | set.mean() |
mean<K extends DataType>(fn: FunctionType<[K], IntegerType | FloatType>): FloatExpr |
Mean with projection | set.mean(($, key) => key.toFloat()) |
| Conversion Operations | ||
toArray<K extends DataType, V extends EastType>(fn?: FunctionType<[K], V>): ArrayExpr<V> |
Convert to array | set.toArray() |
toSet<K extends DataType, U extends DataType>(keyFn?: FunctionType<[K], U>): SetExpr<U> |
Convert to new set (ignoring duplicates) | set.toSet(($, key) => key.multiply(2n)) |
toDict<K extends DataType, K2 extends DataType, V extends EastType>(keyFn?: FunctionType<[K], K2>, valueFn?: FunctionType<[K], V>, onConflictFn?: FunctionType<[V, V, K2], V>): DictExpr<K2, V> |
Convert to dict | set.toDict() |
flattenToArray<K extends DataType, V extends EastType>(fn: FunctionType<[K], ArrayType<V>>): ArrayExpr<V> |
Flatten to array | set.flattenToArray(($, key) => East.Array.range(0n, key)) |
flattenToSet<K extends DataType, U extends DataType>(fn: FunctionType<[K], SetType<U>>): SetExpr<U> |
Flatten to set | set.flattenToSet(($, key) => otherSetDict.get(key)) |
flattenToDict<K extends DataType, K2 extends DataType, V extends EastType>(fn: FunctionType<[K], DictType<K2, V>>, onConflictFn?: FunctionType<[V, V, K2], V>): DictExpr<K2, V> |
Flatten to dict | set.flattenToDict(($, key) => nestedDicts.get(key), ($, v1, v2, k) => v1.add(v2)) |
| Grouping Operations | ||
groupReduce<K extends DataType, K2 extends DataType, V extends EastType, T extends EastType>(keyFn: FunctionType<[K], K2>, valueFn: FunctionType<[K], V>, initFn: FunctionType<[K2], T>, reduceFn: FunctionType<[T, V, K2], T>): DictExpr<K2, T> |
Group by key and reduce groups | set.groupReduce(($, key) => key.remainder(2n), ($, key) => key, ($, grp) => 0n, ($, acc, val, grp) => acc.add(val)) |
groupSize<K extends DataType, K2 extends DataType>(keyFn?: FunctionType<[K], K2>): DictExpr<K2, IntegerType> |
Count elements in each group | set.groupSize(($, key) => key.remainder(2n)) |
groupEvery<K extends DataType, K2 extends DataType>(keyFn: FunctionType<[K], K2>, predFn: FunctionType<[K], BooleanType>): DictExpr<K2, BooleanType> |
Check if all elements in each group match predicate | set.groupEvery(($, key) => key.remainder(2n), ($, key) => East.greater(key, 0n)) |
groupSome<K extends DataType, K2 extends DataType>(keyFn: FunctionType<[K], K2>, predFn: FunctionType<[K], BooleanType>): DictExpr<K2, BooleanType> |
Check if any element in each group matches predicate | set.groupSome(($, key) => key.remainder(2n), ($, key) => East.greater(key, 10n)) |
groupSum<K extends DataType, K2 extends DataType>(keyFn: FunctionType<[K], K2>, valueFn?: FunctionType<[K], IntegerType | FloatType>): DictExpr<K2, IntegerType | FloatType> |
Sum values in each group | set.groupSum(($, key) => key.remainder(2n)) |
groupMean<K extends DataType, K2 extends DataType>(keyFn: FunctionType<[K], K2>, valueFn?: FunctionType<[K], IntegerType | FloatType>): DictExpr<K2, FloatType> |
Mean of values in each group | set.groupMean(($, key) => key.remainder(2n)) |
groupToArrays<K extends DataType, K2 extends DataType, V extends EastType>(keyFn: FunctionType<[K], K2>, valueFn?: FunctionType<[K], V>): DictExpr<K2, ArrayType<V>> |
Collect elements into arrays by group | set.groupToArrays(($, key) => key.remainder(2n)) |
groupToSets<K extends DataType, K2 extends DataType, U extends DataType>(keyFn: FunctionType<[K], K2>, valueFn?: FunctionType<[K], U>): DictExpr<K2, SetType<U>> |
Collect elements into sets by group | set.groupToSets(($, key) => key.remainder(2n)) |
groupToDicts<K extends DataType, K2 extends DataType, K3 extends DataType, V extends EastType>(keyFn: FunctionType<[K], K2>, keyFn2: FunctionType<[K], K3>, valueFn?: FunctionType<[K], V>, combineFn?: FunctionType<[V, V, K3], V>): DictExpr<K2, DictType<K3, V>> |
Collect elements into nested dicts | set.groupToDicts(($, key) => key.remainder(2n), ($, key) => key, ($, key) => key) |
Standard Library: See STDLIB.md for set generation functions.
Dict
Dict expressions (DictExpr<K, V>) represent mutable, sorted key-value mappings with functional operations and flexible merging strategies.
Example:
import { East, DictType, StringType, IntegerType } from "@elaraai/east";
const inventoryLookup = East.function([DictType(StringType, IntegerType), StringType], IntegerType, ($, inventory, item) => {
const count = inventory.get(item, East.function([StringType], IntegerType, ($, key) => 0n));
// Create dict value with $.let() and East.value() (type inference)
const defaults = $.let(East.value(new Map([["apple", 0n], ["banana", 0n]])));
// Alternative: Create dict value with $.let() and East.value() with explicit type
const prices = $.let(East.value(new Map([["apple", 5n]]), DictType(StringType, IntegerType)));
$(inventory.merge("widget", 5n, ($, old, newVal, key) => old.add(newVal)));
const total = inventory.sum();
$.return(count);
});
const compiled = East.compile(inventoryLookup, []);
const inventory = new Map([["apple", 10n], ["banana", 5n]]);
console.log(compiled(inventory, "apple")); // 10n
Operations:
| Signature | Description | Example |
|---|---|---|
| Read Operations | ||
size(): IntegerExpr |
Get dict size | dict.size() |
has<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): BooleanExpr |
Check if key exists | dict.has("foo") |
get<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>): ExprType<V> ❗ |
Get value (errors if missing) | dict.get("foo") |
get<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>, defaultFn: FunctionType<[K], V>): ExprType<V> |
Get value or compute default | dict.get("foo", East.function([StringType], IntegerType, ($, key) => 0n)) |
tryGet<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>): OptionExpr<V> |
Safe get returning Option | dict.tryGet("foo") |
keys<K extends DataType>(): SetExpr<K> |
Get all keys as set | dict.keys() |
getKeys<K extends DataType, V extends EastType>(keys: SetExpr<K>, onMissing?: FunctionType<[K], V>): DictExpr<K, V> |
Get values for given keys | dict.getKeys(keySet, East.function([StringType], IntegerType, ($, key) => 0n)) |
| Mutation Operations | ||
insert<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>, value: ExprType<V> | ValueTypeOf<V>): NullExpr ❗ |
Insert (errors if exists) | dict.insert("foo", 42n) |
insertOrUpdate<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>, value: ExprType<V> | ValueTypeOf<V>): NullExpr |
Insert or update (idempotent) | dict.insertOrUpdate("foo", 42n) |
update<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>, value: ExprType<V> | ValueTypeOf<V>): NullExpr ❗ |
Update existing (errors if missing) | dict.update("foo", 100n) |
merge<K extends DataType, V extends EastType, T2 extends EastType>(key: ExprType<K> | ValueTypeOf<K>, value: T2, updateFn: FunctionType<[V, T2, K], V>, initialFn?: FunctionType<[K], V>): NullExpr |
Merge value with existing using function | dict.merge("count", 1n, ($, old, new, key) => old.add(new), ($, key) => 0n) |
getOrInsert<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>, defaultFn: FunctionType<[K], V>): ExprType<V> |
Get or insert default if missing | dict.getOrInsert("foo", East.function([StringType], IntegerType, ($, key) => 0n)) |
delete<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): NullExpr ❗ |
Delete (errors if missing) | dict.delete("foo") |
tryDelete<K extends DataType>(key: ExprType<K> | ValueTypeOf<K>): BooleanExpr |
Safe delete (returns success) | dict.tryDelete("foo") |
pop<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>): ExprType<V> ❗ |
Remove and return value (errors if missing) | dict.pop("foo") |
swap<K extends DataType, V extends EastType>(key: ExprType<K> | ValueTypeOf<K>, value: ExprType<V> | ValueTypeOf<V>): ExprType<V> ❗ |
Replace and return old value (errors if missing) | dict.swap("foo", 100n) |
clear(): NullExpr |
Remove all entries | dict.clear() |
unionInPlace<K extends DataType, V extends EastType>(other: DictExpr<K, V>, mergeFn?: FunctionType<[V, V, K], V>): NullExpr ❗ |
Union in-place (errors on conflict without mergeFn) | dict.unionInPlace(otherDict, ($, v1, v2, key) => v2) |
mergeAll<K extends DataType, V extends EastType, V2 extends EastType>(other: DictExpr<K, V2>, mergeFn: FunctionType<[V, V2, K], V>, initialFn?: FunctionType<[K], V>): NullExpr |
Merge all entries from another dict | dict.mergeAll(other, ($, cur, new, key) => cur.add(new)) |
| Functional Operations | ||
copy<K extends DataType, V extends EastType>(): DictExpr<K, V> |
Shallow copy | dict.copy() |
map<K extends DataType, V extends EastType, U extends EastType>(fn: FunctionType<[V, K], U>): DictExpr<K, U> |
Transform values (keys unchanged) | dict.map(($, val, key) => val.multiply(2n)) |
filter<K extends DataType, V extends EastType>(predicate: FunctionType<[V, K], BooleanType>): DictExpr<K, V> |
Keep matching entries | dict.filter(($, val, key) => East.greater(val, 0n)) |
filterMap<K extends DataType, V extends EastType, U extends EastType>(fn: FunctionType<[V, K], OptionType<U>>): DictExpr<K, U> |
Filter and map using Option | dict.filterMap(($, val, key) => East.greater(val, 0n) ? East.some(val.multiply(2n)) : East.none()) |
firstMap<K extends DataType, V extends EastType, U extends EastType>(fn: FunctionType<[V, K], OptionType<U>>): OptionExpr<U> |
Map until first successful result | dict.firstMap(($, val, key) => East.greater(val, 10n) ? East.some(val) : East.none()) |
forEach<K extends DataType, V extends EastType>(fn: FunctionType<[V, K], any>): NullExpr |
Execute function for each entry | dict.forEach(($, val, key) => $(arr.pushLast(val))) |
reduce<K extends DataType, V extends EastType, T extends EastType>(fn: FunctionType<[T, V, K], T>, init: T): ExprType<T> |
Fold/reduce over dict | dict.reduce(($, acc, val, key) => acc.add(val), 0n) |
every<K extends DataType, V extends EastType>(fn?: FunctionType<[V, K], BooleanType>): BooleanExpr |
True if all match | dict.every() |
some<K extends DataType, V extends EastType>(fn?: FunctionType<[V, K], BooleanType>): BooleanExpr |
True if any match | dict.some() |
sum<V extends IntegerType | FloatType>(): IntegerExpr | FloatExpr |
Sum of numeric values | dict.sum() |
sum<K extends DataType, V extends EastType>(fn: FunctionType<[V, K], IntegerType | FloatType>): IntegerExpr | FloatExpr |
Sum with projection | dict.sum(($, val, key) => val.multiply(2n)) |
mean<V extends IntegerType | FloatType>(): FloatExpr |
Mean (NaN if empty) | dict.mean() |
mean<K extends DataType, V extends EastType>(fn: FunctionType<[V, K], IntegerType | FloatType>): FloatExpr |
Mean with projection | dict.mean(($, val, key) => val.toFloat()) |
| Conversion Operations | ||
toArray<K extends DataType, V extends EastType, U extends EastType>(fn?: FunctionType<[V, K], U>): ArrayExpr<U> |
Convert to array | dict.toArray() |
toSet<K extends DataType, V extends EastType, U extends DataType>(keyFn?: FunctionType<[V, K], U>): SetExpr<U> |
Convert to set (ignoring duplicates) | dict.toSet(($, val, key) => key) |
toDict<K extends DataType, V extends EastType, K2 extends DataType, V2 extends EastType>(keyFn?: FunctionType<[V, K], K2>, valueFn?: FunctionType<[V, K], V2>, onConflictFn?: FunctionType<[V2, V2, K2], V2>): DictExpr<K2, V2> |
Convert to new dict | dict.toDict(($, val, key) => key) |
flattenToArray<K extends DataType, V extends EastType, U extends EastType>(fn?: FunctionType<[V, K], ArrayType<U>>): ArrayExpr<U> |
Flatten to array | dict.flattenToArray() |
flattenToSet<K extends DataType, V extends EastType, K2 extends DataType>(fn?: FunctionType<[V, K], SetType<K2>>): SetExpr<K2> |
Flatten to set | dict.flattenToSet() |
flattenToDict<K extends DataType, V extends EastType, K2 extends DataType, V2 extends EastType>(fn?: FunctionType<[V, K], DictType<K2, V2>), onConflictFn?: FunctionType<[V2, V2, K2], V2>): DictExpr<K2, V2> |
Flatten to dict | dict.flattenToDict() |
| Grouping Operations | ||
groupReduce<K extends DataType, V extends EastType, K2 extends DataType, U extends EastType, T extends EastType>(keyFn: FunctionType<[V, K], K2>, valueFn: FunctionType<[V, K], U>, initFn: FunctionType<[K2], T>, reduceFn: FunctionType<[T, U, K2], T>): DictExpr<K2, T> |
Group by key and reduce groups | dict.groupReduce(($, val, key) => key.remainder(2n), ($, val, key) => val, ($, grp) => 0n, ($, acc, v, grp) => acc.add(v)) |
groupSize<K extends DataType, V extends EastType, K2 extends DataType>(keyFn?: FunctionType<[V, K], K2>): DictExpr<K2, IntegerType> |
Count elements in each group | dict.groupSize(($, val, key) => key.remainder(2n)) |
groupEvery<K extends DataType, V extends EastType, K2 extends DataType>(keyFn: FunctionType<[V, K], K2>, predFn: FunctionType<[V, K], BooleanType>): DictExpr<K2, BooleanType> |
Check if all elements in each group match predicate | dict.groupEvery(($, val, key) => key.remainder(2n), ($, val, key) => East.greater(val, 0n)) |
groupSome<K extends DataType, V extends EastType, K2 extends DataType>(keyFn: FunctionType<[V, K], K2>, predFn: FunctionType<[V, K], BooleanType>): DictExpr<K2, BooleanType> |
Check if any element in each group matches predicate | dict.groupSome(($, val, key) => key.remainder(2n), ($, val, key) => East.greater(val, 10n)) |
groupSum<K extends DataType, V extends EastType, K2 extends DataType>(keyFn: FunctionType<[V, K], K2>, valueFn?: FunctionType<[V, K], IntegerType | FloatType>): DictExpr<K2, IntegerType | FloatType> |
Sum values in each group | dict.groupSum(($, val, key) => key.remainder(2n)) |
groupMean<K extends DataType, V extends EastType, K2 extends DataType>(keyFn: FunctionType<[V, K], K2>, valueFn?: FunctionType<[V, K], IntegerType | FloatType>): DictExpr<K2, FloatType> |
Mean of values in each group | dict.groupMean(($, val, key) => key.remainder(2n)) |
groupToArrays<K extends DataType, V extends EastType, K2 extends DataType, U extends EastType>(keyFn: FunctionType<[V, K], K2>, valueFn?: FunctionType<[V, K], U>): DictExpr<K2, ArrayType<U>> |
Collect elements into arrays by group | dict.groupToArrays(($, val, key) => key.remainder(2n)) |
groupToSets<K extends DataType, V extends EastType, K2 extends DataType, U extends DataType>(keyFn: FunctionType<[V, K], K2>, valueFn?: FunctionType<[V, K], U>): DictExpr<K2, SetType<U>> |
Collect elements into sets by group | dict.groupToSets(($, val, key) => key.remainder(2n)) |
groupToDicts<K extends DataType, V extends EastType, K2 extends DataType, K3 extends DataType, U extends EastType>(keyFn: FunctionType<[V, K], K2>, keyFn2: FunctionType<[V, K], K3>, valueFn?: FunctionType<[V, K], U>, combineFn?: FunctionType<[U, U, K3], U>): DictExpr<K2, DictType<K3, U>> |
Collect elements into nested dicts | dict.groupToDicts(($, val, key) => key.remainder(2n), ($, val, key) => key, ($, val, key) => val) |
Standard Library: See STDLIB.md for dict generation functions.
Struct
Struct fields are accessed directly as properties.
Example:
const PersonType = StructType({ name: StringType, age: IntegerType });
const updateAge = East.function([PersonType, IntegerType], PersonType, ($, person, yearsToAdd) => {
const newAge = person.age.add(yearsToAdd);
// Create struct value with $.let() and East.value() (type inference)
const updated = $.let(East.value({ ...person, age: newAge }));
// Alternative: Create struct value with $.let() and East.value() with explicit type
const defaultPerson = $.let(East.value({ name: "Unknown", age: 0n }, PersonType));
$.return(updated);
});
const compiled = East.compile(updateAge, []);
const result = compiled({ name: "Alice", age: 30n }, 5n);
console.log(result); // { name: "Alice", age: 35n }
Operations:
| Signature | Description | Example |
|---|---|---|
| Read Operations | ||
field: ExprType<Fields[field]> |
Access struct field | person.name |
Notes:
- Structs are immutable (use spread to create modified copies)
Variant
Variants represent tagged unions (sum types).
Example:
import { East, variant, VariantType, IntegerType, NullType } from "@elaraai/east";
const OptionType = VariantType({ Some: IntegerType, None: NullType });
const processOption = East.function([OptionType], IntegerType, ($, value) => {
// Create variant value with $.let() and East.value() (type inference)
const defaultValue = $.let(East.value(variant("None", null)));
// Alternative: Create variant value with $.let() and East.value() with explicit type
const someValue = $.let(East.value(variant("Some", 10n), OptionType));
// Pattern match using statement form
const result = $.let(0n);
$.match(value, {
Some: ($, x) => $.assign(result, x.add(1n)),
None: $ => $.assign(result, 0n),
});
$.return(result);
});
const compiled = East.compile(processOption, []);
console.log(compiled(variant("Some", 41n))); // 42n
console.log(compiled(variant("None", null))); // 0n
Operations:
| Signature | Description | Example |
|---|---|---|
| Base Operations | ||
match(cases: { [K]: ($, data) => Expr }): ExprType<T> |
Pattern match on all cases | opt.match({ Some: ($, x) => x, None: $ => 0n }) |
unwrap(tag?: string): ExprType<Cases[tag]> ❗ |
Extract value (errors if wrong tag) | opt.unwrap("Some") |
unwrap(tag: string, defaultFn: ($) => Expr): ExprType<Cases[tag]> |
Extract value or compute default | opt.unwrap("Some", $ => 0n) |
getTag(): StringExpr |
Get tag as string | opt.getTag() |
hasTag(tag: string): BooleanExpr |
Check if has specific tag | opt.hasTag("Some") |