Ek

Syntax Reference

Primitives, operators, types, and rules across all three lenses.


1Primitives

# Primitive Real Dev Normie
1Function declaration:: add(ℤ a, ℤ b) -> ℤ {}fn add(int a, int b) -> int {}function add(integer a, integer b) returnType integer {}
2Variable declaration experimental§ ℤ xvar: int xcontainer: integer x
3Assignment experimental§ ℤ x <- 5;var: int x <- 5;container: integer x <- 5;
4Function call↯ add(1, 2);call add(1, 2);runFunction add(1, 2);
5Return type-> ℤ-> intreturnType integer
6Return value↵ x;ret x;return x;
7Conditional? (x > 0) : {} ⋮ {}if (x > 0) then {} else {}if (x > 0) then {} else {}
8Pattern match⋈ x { 1 → ..., _ → ... }match x { 1 -> ..., _ -> ... }check x { 1 means ..., anything means ... }
9Loop↻ i 0..10 {}for i 0..10 {}loop i 0..10 {}
10Record declaration⊞ User { 𝕊 name, ℤ age }rec User { str name, int age }record User { string name, integer age }
11Union declaration⊎ Status { Active, Banned(𝕊) }union Status { Active, Banned(str) }either Status { Active, Banned(string) }
12Array experimental⟦τ ℤ, σ 100⟧ arr;arr[type: int, size: 100] arr;list[type: integer, size: 100];
13Comment (single-line)// comment// comment// comment
14Block body{}{}{}
15Arguments()()()
16Field accessuser.ageuser.ageuser.age
17Array element accessusers @ iusers at iusers at i
18Import※ utils;import utils;import utils;
19Unary negation / not-x / !flag-x / !flag-x / !flag
20Record equalityequals User(a, b) -> 𝔹 { ↵ a.name = b.name; }equals User(a, b) -> bool { ret a.name = b.name; }equals User(a, b) returnType boolean { return a.name = b.name; }
21String concatenationjoin(a, b)join(a, b)join(a, b)
22Record construction↳ User { name: "Sam", age: 25 };new User { name: "Sam", age: 25 };new User { name: "Sam", age: 25 };
23Capability token⊙ FileReadCapability capcap: FileReadCapability capcapability: FileReadCapability cap
24Array literal[90, 85, 76][90, 85, 76][90, 85, 76]
25Separator,,,
26Block comment/* comment *//* comment *//* comment */
27Break⏹;break;break;
28Continue▶;continue;continue;
29While loop (unbounded)⥁ (x > 0) {}while (x > 0) {}whileLoop (x > 0) {}
30Void / no return value▢ greet() {}void greet() {}void greet() {}
31Char type usage§ ℂ c <- 'a';var: char c <- 'a';container: character c <- 'a';
32Statement terminator;;;
33String escapes"line\n\"quoted\"""line\n\"quoted\"""line\n\"quoted\""
34Scientific notation1e101e101e10
35Hex / binary / octal0x1F / 0b1010 / 0o170x1F / 0b1010 / 0o170x1F / 0b1010 / 0o17
36Inline conditional expression§ ℤ max <- ? (a > b) : { ↵ a } ⋮ { ↵ b };var: int max <- if (a > b) then { ret a } else { ret b };container: integer max <- if (a > b) then { return a } else { return b };
37Multi-dimensional array⟦τ ⟦τ ℤ, σ 10⟧, σ 10⟧ grid;arr[type: arr[type: int, size: 10], size: 10] grid;list[type: list[type: integer, size: 10], size: 10] grid;
38Vector (growable) experimental⦃τ ℤ⦄ nums;vec[type: int] nums;vector[type: integer] nums;

2Operators

Operator Real Dev Normie
Add+++
Subtract---
Multiply***
Divide///
Modulo%%%
Equal to===
Not equal!= alt: ≠!=!=
Greater than>>>
Less than<<<
Greater or equal>=>=>=
Less or equal<=<=<=
And& alt: ∧&& experimental&
Or| alt: ∨|| experimental|
Not!!!
Assignment<-<-<-
Bitwise — experimental
Bitwise AND&bitwiseAnd
Bitwise OR|bitwiseOr
Bitwise XOR^bitwiseXor
Bitwise NOT~bitwiseNot
Left shift<<shiftLeft
Right shift>>shiftRight

3Primitive Types

Type Real Dev Normie
Integer (signed, default 32-bit) (= ℤ32)int (= i32)integer (= integer32)
Integer (signed, explicit width)ℤ8 ℤ16 ℤ32 ℤ64i8 i16 i32 i64integer8 integer16 integer32 integer64
Integer (unsigned, default 32-bit) (= ℕ32)u32wholeNumber (= wholeNumber32)
Integer (unsigned, explicit width)ℕ8 ℕ16 ℕ32 ℕ64u8 u16 u32 u64wholeNumber8 … wholeNumber64
Float (default 32-bit) (= ℝ32)float (= f32)float (= float32)
Float (explicit width)ℝ32 ℝ64f32 f64float32 float64
Boolean𝔹boolboolean
String𝕊strstring
Charcharcharacter
Nullnullnull
Array (fixed, bounded) experimental⟦ τ type, σ size ⟧arr[type: t, size: n]list[type: T, size: N]
Vector (growable, audited) experimental⦃ τ type ⦄vec[type: t]vector[type: T]

4Master Symbol Table

Meaning Real Dev Normie
Function declaration::fnfunction
Variable declaration experimental§var:container:
Type label (in array) experimentalτtype:type:
Size label (in array) experimentalσsize:size:
Assignment<-<-<-
Return type in signature->->returnType
Return value from bodyretreturn
Function callcallrunFunction
Conditional?ifif
Then (conditional branch):thenthen
Else (conditional branch)elseelse
Statement terminator;;;
Pattern matchmatchcheck
Loop (bounded)forloop
While loop (unbounded)whilewhileLoop
Breakbreakbreak
Continuecontinuecontinue
Record declarationrecrecord
Union declarationunioneither
Record constructionnewnew
Outcome handlingverifyverify
Capability tokencap:capability:
Importimportimport
Void / no return valuevoidvoid
Array⟦ ⟧arr[]list[]
Vector⦃ ⦄vec[]vector[]
Field access...
Array element access@atat
Separator,,,
Block body{}{}{}
Arguments()()()
Comment//////
Block comment/* *//* *//* */
True experimentaltruetrue
False experimentalfalsefalse
Integer typeintinteger
Unsigned integer typeu32wholeNumber
Float typefloatfloat
Boolean type𝔹boolboolean
String type𝕊strstring
Char typecharcharacter
Nullnullnull

5Outcome Handling

A fallible operation returns a plain two-variant union (Success / Failure), using the existing union declaration — no generics, no angle brackets. The caller handles both branches with the dedicated verify construct.

Declaring the union:

 Outcome { Success(𝕊), Failure(FileNotFound) }
Real
  readFile(path) {
    success(contents) →  contents;
    failure(err) →  "error occurred";
}
Dev
verify call readFile(path) {
    success(contents) -> ret contents;
    failure(err) -> ret "error occurred";
}
Normie
verify runFunction readFile(path) {
    success(contents) means return contents;
    failure(err) means return "error occurred";
}

success(...) and failure(...) are fixed labels, identical across all three lenses.


6Operator Precedence

Ek has no implicit operator precedence table. Any expression mixing more than one category of operator (arithmetic, comparison, logical) must use explicit parentheses. This rule is identical across all three lenses.

(a + b) > c        // valid
a + b > c          // rejected — ambiguous, no implicit precedence

(a & b) | c        // valid
a & b | c          // rejected — ambiguous

7Worked Example

Full-coverage example showing every major construct across all three lenses.

 utils;

/* User record: stores a name and an age,
   used across the file read and greeting logic below */
 User { 𝕊 name,  age };

 Outcome { Success(𝕊), Failure(FileNotFound) };

equals User(a, b) -> 𝔹 {
     a.name = b.name;
}

:: greet(§ User u) -> 𝕊 {
    § 𝕊 greeting <- join("Hello, ", u.name);
     greeting;
}

:: readUserFile(§ User u,  FileReadCapability cap) -> 𝕊 {
      readFile(u.name, cap) {
        success(contents) →  contents;
        failure(err) →  "could not read file";
    }
}

:: isAdult(§ User u) -> 𝔹 {
    ? (u.age >= 18) : {
         ;
    }  {
         ;
    }
}

:: printVoid() ->  {
     logMessage("done");
}

:: firstAdultName(§ ⟦τ User, σ 5⟧ users) -> 𝕊 {
    §  i <- 0;

     (i < 5) {
        ? ( isAdult(users @ i)) : {
             (users @ i).name;
        }  {
            i <- i + 1;
        }
    }

     "no adult found";
}

:: countVowels(§ 𝕊 word) ->  {
    §  count <- 0;
    §  i <- 0;

     i 0..10 {
        ? ((word @ i) = 'a') : {
            count <- count + 1;
        }  {
            count <- count;
        }

        ? (i = 9) : {
            ;
        }  {
            ;
        }
    }

     count;
}

:: oldestUser(§ ⟦τ User, σ 3⟧ users) -> User {
    §  oldestIndex <- 0;
    §  i <- 0;

     i 0..3 {
        ? ((users @ i).age > (users @ oldestIndex).age) : {
            oldestIndex <- i;
        }  {
            oldestIndex <- oldestIndex;
        }
    }

     users @ oldestIndex;
}

:: main( FileReadCapability cap) ->  {
    § User sam <-  User { name: "Sam", age: 25 };
    § User alex <-  User { name: "Alex", age: 31 };
    § User priya <-  User { name: "Priya", age: 42 };

    § ⟦τ User, σ 3⟧ users <- [sam, alex, priya];
    § ⟦τ ⟦τ ℤ, σ 3⟧, σ 3⟧ grid <- [[1, 2, 3], [4, 5, 6], [7, 8, 9]];

    §  initial <- 'S';
    §  hexValue <- 0x1F;
    §  bigNumber <- 1e10;

    § 𝕊 greeting <-  greet(sam);
    § 𝔹 samIsAdult <-  isAdult(sam);
    § User oldest <-  oldestUser(users);
    §  max <- ? (sam.age > alex.age) : {  sam.age; }  {  alex.age; };

     oldest.name {
        "Priya" 1;
        _ →  0;
    }
}

8Code Rules

These rules cover both lens-specific conventions and behaviors that apply identically across all three lenses.

  • 1Variables are called containers — declared as container: Type name
  • 2experimental container:, type:, and size: map to Real Code symbols (§, τ, σ) and Dev keywords (var:, type:, size:) — fully translatable across all three lenses
  • 3Assignment uses <-; functions use returnType and return; calls use runFunction
  • 4Conditionals use then before the block — if (x > 0) then {} else {} — Dev Lens shares this same then/else structure
  • 5Types are full English words — integer, string, float, boolean, null, character
  • 6Capability tokens use the label capability: (Dev uses the shorter cap:)
  • 7whileLoop is the Normie keyword for the unbounded loop; loop remains the keyword for bounded range iteration
  • 8{}, (), and // are kept as-is across all lenses
  • 9No any or wildcard types — unknown types are a compile error, not a wildcard
  • 10Bitwise operators are experimental, pending final design and model testing
  • 11No implicit operator precedence — mixed-category expressions (a + b > c) require explicit parentheses ((a + b) > c) in every lens
  • 12- and ! double as unary prefix operators (-x, !flag) in addition to their binary forms — unambiguous from grammatical position
  • 13Imports are resolved at compile time, never as text substitution
  • 14Record construction uses new TypeName { field: value, ... } ( in Real Code) — named fields, mandatory declaration order, no positional shorthand
  • 15Capability tokens are declared like any typed parameter, but with their own dedicated label/symbol ( in Real Code) to flag that the function touches the outside world
  • 16Array literals use plain [ ] with comma-separated values — identical across all three lenses
  • 17Block comments use /* ... */, identical across all three lenses; // remains for single-line comments
  • 18Every statement ends with ; — the universal statement terminator, identical across all three lenses. Two constructs are exempt because they are definitions, not statements: a function declaration (::/fn/function) and a standalone control-flow block (if/while/for/verify/match) used in statement position — both self-terminate at } with no additional ;. Everything else that is a statement ends in ;, including record/union declarations and any statement whose right-hand side happens to end in } such as an inline conditional used as a value.
  • 19& and | short-circuit, matching C/Rust convention — the right side is skipped once the left side already determines the result
  • 20break and continue only affect the single nearest enclosing loop — no labeled/multi-level jump
  • 21void marks a function that returns nothing ( in Real Code) — a deliberate, familiar exception to the "no special cases" rule
  • 22character () is a distinct type from string — needed for kernel/firmware/device-driver layer coverage where single-byte precision matters
  • 23No function overloading — one name, one function, always
  • 24main is the required, fixed-name program entry point, identical across all three lenses
  • 25Declaration before use is required — a function or type must be declared before any point in the file where it is referenced, unlike Rust's order-independence
  • 26Whitespace and indentation are purely cosmetic and never affect meaning — block delimiters ({}) already make indentation-based structure unnecessary
  • 27A declared array size of 0 is a compile error
  • 28String literals support backslash escapes (\n, \t, \", \\), scientific notation (1e10), and hex/binary/octal integer literals (0x1F, 0b1010, 0o17)
  • 29String literals may span multiple lines directly in the source
  • 30Inline conditional expressions reuse the existing if/then/else construct as a value — no separate ternary operator
  • 31There is no separate mutable/immutable distinction — linear types mean every value is used exactly once and never reassigned
  • 32Multi-dimensional arrays and nested records require no new syntax
  • 33Recursion requires no new syntax — a function calling itself is an ordinary function call
  • 34Integers default to signed, 32-bit ( = ℤ32); unsigned integers use a distinct base symbol (); floats default to 32-bit ( = ℝ32)
  • 35Vector (⦃ ⦄) is the one place unbounded allocation is permitted. A record's "next" or "children" field is a plain integer index into a Vector, not a pointer. Growth happens through one audited reallocation mechanism in the standard library.
  • 36Capturing closures. Whether a closure can capture a variable from its enclosing scope is undecided. The core difficulty is the interaction with linear types and capability tokens — it's unclear whether a captured linear value or capability token should be considered consumed when the closure is created, or each time the closure is called. Deferred pending further design work.