# EK9 — Full Language Reference for AI > Structured reference for AI coding assistants working with EK9 Generated from `forAI.json`. The concise index is at https://ek9.io/llms.txt. The Q&A corpus is published separately at https://ek9.io/qa/ (machine-readable: https://ek9.io/qa/qa.jsonl). ## Excluded Features - **Description**: Features deliberately excluded from EK9 based on production bug evidence - **Features** - **Keyword**: break - **Error Code**: E01070 - **Error URL**: https://ek9.io/errors.html#E01070 - **Reason**: 15% of C# bugs (Microsoft 2011), 200+ Linux kernel CVEs - **Alternative**: Stream pipelines with 'head' or guard expressions - **Example** - **Wrong**: ```ek9 for item in items if item.matches() break ``` - **Correct**: ```ek9 result <- cat items | filter by matches | head ``` - **Documentation**: https://ek9.io/flowControl.html#streams - **Keyword**: continue - **Error Code**: E01071 - **Error URL**: https://ek9.io/errors.html#E01071 - **Reason**: 15% of C# bugs (Microsoft 2011), 3x higher defect density - **Alternative**: Stream pipelines with 'filter' or 'reject' - **Example** - **Wrong**: ```ek9 for item in items if not item.isValid() continue process(item) ``` - **Correct**: ```ek9 cat items | filter by isValid | for each as item process(item) ``` - **Documentation**: https://ek9.io/flowControl.html#streams - **Keyword**: return - **Error Code**: E01072 - **Error URL**: https://ek9.io/errors.html#E01072 - **Reason**: Apple SSL bug (2014), 23% of resource leaks (Google study) - **Alternative**: Return declarations with '<-' and guard expressions - **Example** - **Wrong**: ```ek9 process() -> content as String <- result as String? if not content? return "" ``` - **Correct**: ```ek9 process() -> content as String <- result as String: String() if content? result: transform(content) ``` - **Documentation**: https://ek9.io/flowControl.html#guards - **Keyword**: null - **Error Code**: E01073 - **Error URL**: https://ek9.io/errors.html#E01073 - **Reason**: Billion-dollar mistake (Hoare), #1 Java exception - **Alternative**: Tri-state semantics (absent/unset/set) with '?' operator - **Example** - **Wrong**: ```ek9 name <- null ``` - **Correct**: ```ek9 name <- String() // Creates UNSET String if name? process(name) // Safe - guaranteed set ``` - **Documentation**: https://ek9.io/basics.html#tri-state - **Keyword**: ; - **Name**: semicolon - **Error Code**: E01074 - **Error URL**: https://ek9.io/errors.html#E01074 - **Reason**: Indentation-based syntax (like Python) - **Alternative**: Simply remove - EK9 uses newlines and indentation - **Example** - **Wrong**: ```ek9 name <- "Steve"; ``` - **Correct**: ```ek9 name <- "Steve" ``` - **Documentation**: https://ek9.io/structure.html#indentation - **Keyword**: new - **Error Code**: E01075 - **Error URL**: https://ek9.io/errors.html#E01075 - **Reason**: EK9 uses TypeName() directly without 'new' keyword - **Alternative**: Call type constructor directly: Person() instead of new Person() - **Example** - **Wrong**: ```ek9 person <- new Person("Steve") ``` - **Correct**: ```ek9 person <- Person("Steve") ``` - **Documentation**: https://ek9.io/basics.html#construction - **Keyword**: goto - **Error Code**: E01076 - **Error URL**: https://ek9.io/errors.html#E01076 - **Reason**: Dijkstra (1968) 'Go To Statement Considered Harmful', Apple SSL bug (2014) - **Alternative**: Use structured control flow: try/finally, guard expressions - **Example** - **Wrong**: ```ek9 if error goto cleanup ``` - **Correct**: ```ek9 try riskyOperation() finally cleanup() ``` - **Documentation**: https://ek9.io/flowControl.html#structured - **Keyword**: def - **Error Code**: E01077 - **Error URL**: https://ek9.io/errors.html#E01077 - **Reason**: EK9 defines functions by name directly without keyword (Python syntax excluded) - **Alternative**: Start with function name directly, use -> for parameters, <- for returns - **Example** - **Wrong**: ```ek9 def greet(name as String) stdout.println("Hello " + name) ``` - **Correct**: ```ek9 greet() -> name as String stdout.println("Hello " + name) ``` - **Documentation**: https://ek9.io/functions.html - **Keyword**: elif - **Error Code**: E01078 - **Error URL**: https://ek9.io/errors.html#E01078 - **Reason**: EK9 uses 'else if' as two words for readability (Python syntax excluded) - **Alternative**: Use 'else if' (two separate words) - **Example** - **Wrong**: ```ek9 if x > 10 big() elif x > 5 medium() ``` - **Correct**: ```ek9 if x > 10 big() else if x > 5 medium() ``` - **Documentation**: https://ek9.io/flowControl.html#if - **Keyword**: None - **Error Code**: E01079 - **Error URL**: https://ek9.io/errors.html#E01079 - **Reason**: Python's None equivalent - EK9 uses tri-state semantics instead - **Alternative**: Use TypeName() for unset values, '?' operator to check if set - **Example** - **Wrong**: ```ek9 name <- None ``` - **Correct**: ```ek9 name <- String() // Creates UNSET String if name? process(name) ``` - **Documentation**: https://ek9.io/basics.html#tri-state - **Keyword**: self - **Error Code**: E01080 - **Error URL**: https://ek9.io/errors.html#E01080 - **Reason**: EK9 uses 'this' instead of 'self' (Java/C++/JavaScript convention) - **Alternative**: Use 'this' keyword, or omit when unambiguous - **Example** - **Wrong**: ```ek9 greet() stdout.println("I am " + self.name) ``` - **Correct**: ```ek9 greet() stdout.println("I am " + this.name) // Or simply: "I am " + name ``` - **Documentation**: https://ek9.io/classes.html#this - **Keyword**: listComprehension - **Name**: Python list comprehension - **Pattern**: [x for x in items] - **Error Code**: E01081 - **Error URL**: https://ek9.io/errors.html#E01081 - **Reason**: EK9 uses stream pipelines for composability and lazy evaluation - **Alternative**: Define function for transformation, use 'cat collection | map with fn | collect as List' pattern - **Example** - **Wrong**: ```ek9 squares <- [x * x for x in numbers] ``` - **Correct**: ```ek9 squareIt() -> n as Integer <- result as Integer: n * n squares <- cat numbers | map with squareIt | collect as List of Integer ``` - **Documentation**: https://ek9.io/streamsAndPipelines.html - **Keyword**: multipleArgumentArrows - **Name**: multiple '->' arrows for parameters - **Pattern**: ```ek9 -> param1 as T1 -> param2 as T2 ``` - **Error Code**: E01082 - **Error URL**: https://ek9.io/errors.html#E01082 - **Reason**: EK9 uses ONE '->' with indented block for multiple parameters - **Alternative**: Use single '->' followed by indented parameter block - **Example** - **Wrong**: ```ek9 badFunction() -> param1 as String -> param2 as Integer <- result as String ``` - **Correct**: ```ek9 goodFunction() -> param1 as String param2 as Integer <- result as String ``` - **Documentation**: https://ek9.io/functions.html#parameters - **Keyword**: multipleReturnArrows - **Name**: multiple '<-' arrows for returns - **Pattern**: ```ek9 <- result1 as T1 <- result2 as T2 ``` - **Error Code**: E01083 - **Error URL**: https://ek9.io/errors.html#E01083 - **Reason**: EK9 functions have exactly ONE return declaration - use records for multiple values - **Alternative**: Create a record type and return a single instance containing multiple fields - **Example** - **Wrong**: ```ek9 badFunction() -> input as String <- result1 as String <- result2 as Integer ``` - **Correct**: ```ek9 defines record ReturnValues text as String? count as Integer? defines function goodFunction() -> input as String <- rtn as ReturnValues: ReturnValues(input, length(input)) ``` - **Documentation**: https://ek9.io/functions.html#returns - **Keyword**: instanceof - **Name**: type checking - **Reason**: Breaks polymorphism, creates brittle code, causes maintenance bugs - **Alternative**: Use 'dispatcher' methods for type-specific behavior - **Note**: There is NO way to check types at runtime in EK9 - this is by design - **Documentation**: https://ek9.io/advancedClassMethods.html#dispatcher - **Keyword**: casting - **Name**: type casting - **Reason**: Breaks type safety, enables ClassCastException-style bugs - **Alternative**: Use 'dispatcher' methods - compiler routes to correct overload - **Note**: There is NO way to cast types in EK9 - this is by design - **Documentation**: https://ek9.io/advancedClassMethods.html#dispatcher - **Keyword**: const - **Name**: constant data modifier - **Reason**: EK9 takes a fundamentally different approach: the constraint is on the OPERATION, not the data - **Alternative**: Mark functions/methods 'as pure' — compiler prevents all mutation in pure contexts at compile time - **Note**: Data is NEVER const in EK9. Immutability is contextual: the same List is unmutatable in a pure function but fully mutable in a non-pure function. This preserves the Liskov Substitution Principle — Java's immutable collections break LSP by throwing runtime UnsupportedOperationException - **Documentation**: https://ek9.io/basics.html#pure - **Keyword**: final - **Name**: final/sealed modifier - **Reason**: EK9 types are CLOSED by default (no 'final' needed). For data immutability, use 'as pure' on operations - **Alternative**: Types are closed by default — use 'as open' to allow extension. Use 'as pure' for mutation control - **Note**: Two concepts combined in other languages: (1) Extension prevention — EK9 types are closed by default, use 'as open' to opt in. (2) Data immutability — use 'as pure' on functions/methods, not 'final' on data - **Documentation**: https://ek9.io/classes.html - **Keyword**: immutable - **Name**: immutable data types - **Reason**: EK9 has NO immutable List, Dict, record, or any data type — and never will. ALL types are mutable - **Alternative**: Mark functions/methods 'as pure' — compiler prevents mutation at compile time, not runtime - **Note**: Java's immutable List breaks the Liskov Substitution Principle by throwing UnsupportedOperationException at runtime. EK9 prevents mutation at COMPILE TIME instead. The same object can be: unmutatable when passed to a pure function, fully mutable when passed to a non-pure function. Immutability is contextual, not permanent - **Documentation**: https://ek9.io/basics.html#pure - **Keyword**: classBraces - **Name**: Java/C# class definition with braces and access modifiers - **Reason**: EK9 class definitions use INDENTATION (like Python), not braces. Fields are ALWAYS private (no access modifier keyword). Methods do NOT use 'public', 'private', 'protected', 'void', or 'this.' prefix. Method signatures use '-> arg as Type' for parameters and '<- result as Type' for return values. NO 'new Type()' for construction — just 'Type()'. - **Alternative**: Use indentation-based EK9 class syntax. Fields declared bare. Constructor name matches class name. Methods use '-> arg as Type' / '<- result as Type'. Field access without 'this.' prefix. - **Example** - **Wrong**: ```ek9 defines class UniqueNames { private List names = new List(); public void add(String name) { if (!names.contains(name)) { names.add(name); } } public int count() { return names.size(); } } ``` - **Correct**: ```ek9 defines class UniqueNames names as List of String UniqueNames() names: List() of String add() -> name as String if not name in names names += name count() as pure <- rtn as Integer: names.length() ``` - **Rule**: EK9 has NO { } braces, NO 'public/private/protected', NO 'void', NO 'this.', NO 'new'. Indentation is the structure. Fields are bare names. Constructor identical to class name. '-> ' for params, '<- ' for return. - **Documentation**: https://ek9.io/structure.html#class - **Keyword**: switchCStyle - **Name**: C/Java switch with parens, braces, colons, and return statements - **Reason**: EK9 switch uses indentation (no braces), no parens around argument, no colons after case labels. Switch CAN BE an expression — its result assigned to a variable. Multiple values per case via comma: 'case 1, 2, 3'. NO 'return' statement — assign to declared return variable: 'rtn: value'. NO fall-through (each case is independent by design). - **Alternative**: Use the EK9 indentation-based switch with bare 'case x' (no colon). Body of each case indents. Assign result to a variable for switch-as-expression, or assign to outer return variable for switch-as-statement. - **Example** - **Wrong**: ```ek9 function findLabel(code: Integer): String { switch(code) { case 1: return "active"; case 2: return "inactive"; default: return "unknown"; } } ``` - **Correct**: ```ek9 findLabel() as pure -> code as Integer <- rtn as String? switch code case 1 rtn: "active" case 2 rtn: "inactive" default rtn: "unknown" ``` - **Rule**: EK9 switch: NO parens around argument, NO braces, NO colons after case/default labels, NO 'return' statement. Body of each case indents under bare 'case x'. Switch can be an expression: 'result <- switch x' with new variable, or assign in each case to an outer return variable. - **Documentation**: https://ek9.io/flowControl.html#switch - **Keyword**: lowercasePrimitives - **Name**: Java/C# lowercase primitive type names - **Reason**: EK9 primitive types are PascalCase: Integer, String, Boolean, Float, Date, Money. There are NO 'int', 'string', 'bool', 'float' lowercase primitives. EK9 wraps these as proper objects with built-in operators (==, <=>, $, #?, ?, etc.) and tri-state semantics (absent/unset/set). - **Alternative**: Use PascalCase EK9 type names: Integer not int, String not string, Boolean not bool, Float not float. These are full objects with operator support, not primitive values. - **Example** - **Wrong**: ```ek9 function add(a: int, b: int): int { return a + b; } ``` - **Correct**: ```ek9 add() as pure -> a as Integer -> b as Integer <- rtn as Integer: a + b ``` - **Rule**: EK9 primitive type names are PascalCase (Integer, String, Boolean, Float, Date). Lowercase 'int'/'string'/'bool'/'float' do NOT exist. The PascalCase types are full objects with built-in operators and tri-state semantics. - **Documentation**: https://ek9.io/standardTypes.html ## Dispatcher Pattern - **Description**: EK9's replacement for instanceof/casting - the ONLY way to handle type-specific behavior - **Critical Rule**: There is NO instanceof and NO casting in EK9. Dispatcher is the ONLY mechanism. - **How It Works**: Mark a method 'as dispatcher', provide overloaded methods for specific types, compiler generates dispatch logic - **Why Methods Only**: Functions cannot be overloaded (names must be unique in module), so dispatcher only works on methods - **Single Dispatch** - **Description**: Dispatch on one parameter's runtime type - **Example**: ```ek9 render() as dispatcher -> shape as Shape shape.draw() render() -> circle as Circle // Circle-specific rendering ``` - **Double Dispatch** - **Description**: Dispatch on two parameters' runtime types (visitor pattern replacement) - **Example**: ```ek9 intersect() as dispatcher -> s1 as Shape -> s2 as Shape <- result as Intersection? intersect() -> s1 as Circle -> s2 as Circle <- result as Intersection: ArcIntersection() ``` - **Ambiguity Detection** - **Description**: Compiler detects ambiguous dispatch and requires explicit resolution - **Example**: ```ek9 If Square implements T1 and T2, and you have render(T1) and render(T2), compiler requires render(Square) to disambiguate ``` - **Fallback Behavior**: If no specific type match exists, the dispatcher's own implementation is called as default - **Related Errors** - E07120 - E07820 - E05170 - E05180 - **Documentation**: https://ek9.io/advancedClassMethods.html#dispatcher ## Tri State Semantics - **Description**: EK9's tri-state object model replaces null - **States** - **Name**: absent - **Meaning**: Object doesn't exist - **Example**: ```ek9 Dict key not found ``` - **Name**: unset - **Meaning**: Object exists but has no meaningful value - **Example**: ```ek9 String() creates unset String ``` - **Name**: set - **Meaning**: Object exists with valid, usable value - **Example**: ```ek9 String("hello") is set ``` - **Critical Rule**: Collections (List, Dict) are ALWAYS set when created, even if empty. Only primitives (String, Integer) are unset when empty. - **Operator**: ? - **Operator Meaning**: Calls _isSet() method to check if value is set - **Documentation**: https://ek9.io/basics.html#tri-state ## Closed By Default - **Description**: EK9 types are closed by default (like Kotlin) - **Error Code**: E05030 - **Error URL**: https://ek9.io/errors.html#E05030 - **Never Extendable** - List - Dict - DictEntry - MutexLock - Optional - PriorityQueue - Result - **How To Make Extendable**: Declare with 'as open' modifier - **Correct Pattern**: Use composition/delegation instead of inheritance - **Example** - **Wrong**: ```ek9 MyList extends List of String ``` - **Correct**: ```ek9 MyList items as List of String add(item as String) items += item ``` - **Documentation**: https://ek9.io/inheritance.html#closed-by-default ## Access Modifiers - **Description**: EK9 access modifier rules vary by declaration kind - fields, methods, and operators each have different rules - **Critical Rule**: Fields are ALWAYS private in classes/components (no syntax to change this). Only METHODS support public/protected/private modifiers. - **Fields** - **Rule**: Fields (properties) have NO access modifier in the grammar - **Class Fields**: Always private - subclasses cannot access parent fields directly - **Record Fields**: Always public - records are data structures with public access - **Component Fields**: Always private - same as class fields - **Trait Fields**: Traits cannot have fields - **Package Properties**: Always public within module scope - **No Protected Fields**: There is no way to declare a protected field - this is deliberate to avoid the fragile base class problem - **Subclass Access**: Use accessor methods (public or protected) to expose parent state to subclasses - **Methods** - **Rule**: Methods support optional access modifiers - **Syntax**: (OVERRIDE | DEFAULT)? accessModifier? identifier ... - **Default Access**: Public (no modifier needed) - **Options** - public - protected - private - **Protected Methods**: Accessible from subclasses and same-hierarchy types - **Private Methods**: Accessible only within the declaring type - **Operators** - **Rule**: Operators have NO access modifier - always public - **Syntax**: (OVERRIDE | DEFAULT)? OPERATOR operator ... - **Rationale**: Operators define the public contract of a type - **Example** - **Wrong**: ```ek9 defines class MyClass as open protected name <- String() //WRONG: grammar error, fields cannot have access modifiers ``` - **Correct**: ```ek9 defines class MyClass as open name <- String() //Field is always private protected getName() as pure //Use protected method for subclass access <- rtn as String: name ChildClass extends MyClass describe() <- rtn as String: getName() //Access parent state via method ``` - **Error Codes** - **E06180**: Field or method not accessible from this context (private access violation) - **Documentation**: https://ek9.io/advancedClassMethods.html ## Assignment Operators - **Description**: Three distinct assignment operators with precise semantics - **Operators** - **Operator**: <- - **Name**: Declaration - **Semantics**: Create NEW variable + first assignment - **Use Case**: First-time assignment - **Operator**: := - **Name**: Assignment - **Semantics**: Assign to EXISTING variable - **Use Case**: Reassignment - **Operator**: :=? - **Name**: Guarded Assignment - **Semantics**: Only assign if variable is UNSET - **Use Case**: Conditional initialization - **Documentation**: https://ek9.io/basics.html#assignments ## Guard Expressions - **Description**: Guards combine assignment with null/isSet checking - **Syntax**: if variable <- expression - **Meaning**: Execute block only if expression result is SET - **Works In** - if - switch - for - while - try - **Examples** - **Construct**: if - **Code**: ```ek9 if name <- getName() stdout.println(name) ``` - **Construct**: for - **Code**: ```ek9 for item <- iterator.next() process(item) ``` - **Construct**: while - **Code**: ```ek9 while conn <- getActiveConnection() transferData(conn) ``` - **Documentation**: https://ek9.io/flowControl.html#guards ## Quality Metrics - **Description**: Compile-time enforced quality thresholds - **Metrics** - **Name**: Cyclomatic Complexity - **Abbreviation**: CC - **Good**: <=10 - **Monitor**: 11-15 - **Warning**: 16-25 - **Limit**: 45 - **Error Code**: E11010 - **Error URL**: https://ek9.io/errors.html#E11010 - **Name**: Cognitive Complexity - **Abbreviation**: Cog - **Good**: <=10 - **Monitor**: 11-15 - **Warning**: 16-25 - **Limit**: 35 - **Error Code**: E11021 - **Error URL**: https://ek9.io/errors.html#E11021 - **Name**: Nesting Depth - **Good**: <=2 - **Monitor**: 3 - **Warning**: 4-5 - **Limit**: 6 - **Error Code**: E11011 - **Error URL**: https://ek9.io/errors.html#E11011 - **Name**: Statement Count - **Good**: <=50 - **Monitor**: 51-100 - **Warning**: 101-150 - **Limit**: 150 - **Error Code**: E11012 - **Error URL**: https://ek9.io/errors.html#E11012 - **Name**: Cohesion (LCOM4) - **Good**: <=3 - **Monitor**: 4-5 - **Warning**: 6-8 - **Limit**: 8 - **Error Code**: E11014 - **Error URL**: https://ek9.io/errors.html#E11014 - **Name**: Coupling (Ce) - **Good**: <=5 - **Monitor**: 6-8 - **Warning**: 9-12 - **Limit**: 12 - **Error Code**: E11015 - **Error URL**: https://ek9.io/errors.html#E11015 - **Name**: Inheritance Depth - **Good**: <=2 - **Monitor**: 3 - **Warning**: 4 - **Limit**: 4 - **Error Code**: E11019 - **Error URL**: https://ek9.io/errors.html#E11019 - **Name**: Coverage - **Good**: >=80% - **Monitor**: 60-79% - **Warning**: <60% - **Limit**: 80% for publishing - **Documentation**: https://ek9.io/quality.html ## Banned Identifiers - **Description**: Variable names banned due to high defect correlation. List mirrors NonDescriptiveNameOrError.TIER1_BANNED in compiler-main. - **Banned** - **Name**: temp - **Defect Multiplier**: 3.1 - **Name**: tmp - **Defect Multiplier**: 3.1 - **Name**: data - **Defect Multiplier**: 2.8 - **Name**: dat - **Defect Multiplier**: 2.8 - **Name**: flag - **Defect Multiplier**: 3.4 - **Name**: flg - **Defect Multiplier**: 3.4 - **Name**: value - **Defect Multiplier**: 2.9 - **Name**: val - **Defect Multiplier**: 2.9 - **Name**: object - **Defect Multiplier**: 3.0 - **Name**: obj - **Defect Multiplier**: 3.0 - **Name**: buffer - **Defect Multiplier**: 2.7 - **Name**: buf - **Defect Multiplier**: 2.7 - **Exceptions** - Single-character names (x, y, i, j, T, K, V) are always allowed - **Error Code**: E11031 - **Documentation**: https://ek9.io/quality.html#naming-quality ## Code Quality Enforcement - **Description**: Compile-time code quality rules unique to EK9. These exist in NO other language — AI will violate them on every attempt unless explicitly told. Ordered by AI violation frequency. - **String Interpolation Mandate** - **Error Code**: E11068 - **Rule**: 3+ part string concatenation is a compile error - **Rationale**: Forces readable interpolation, prevents unbounded concatenation chains - **Example** - **Wrong**: ```ek9 result <- first + " " + last ``` - **Correct**: ```ek9 result <- `${first} ${last}` ``` - **Note**: 2-part concatenation (a + b) is allowed. 3+ parts must use backtick interpolation. - **Named Argument Enforcement** - **Rules** - **Error Code**: E11062 - **Rule**: 4+ positional arguments require named arguments - **Example** - **Wrong**: ```ek9 connect("db", 5432, true, false) ``` - **Correct**: ```ek9 connect(host: "db", port: 5432, ssl: true, compress: false) ``` - **Error Code**: E11061 - **Rule**: 2+ Boolean literal arguments require named arguments - **Example** - **Wrong**: ```ek9 configure(true, false) ``` - **Correct**: ```ek9 configure(verbose: true, dryRun: false) ``` - **Syntax**: paramName: value - **Note**: No other language mandates named arguments. AI training data contains zero examples of this constraint. - **Magic Literal Detection** - **Rules** - **Error Code**: E11064 - **Rule**: Bare numeric literal in comparison is a compile error - **Example** - **Wrong**: ```ek9 if retries > 3 timeout() ``` - **Correct**: ```ek9 maxRetries <- 3 if retries > maxRetries timeout() ``` - **Error Code**: E11065 - **Rule**: Same literal 3+ times per file or 4+ per module requires named constant - **Example** - **Wrong**: ```ek9 x * 3.14 y * 3.14 z * 3.14 ``` - **Correct**: ```ek9 pi <- 3.14159 x * pi y * pi z * pi ``` - **Exempt Values** - 0 - 1 - -1 - 0.0 - 1.0 - **Exempt Types** - Boolean - Character - RegEx - Binary - **Note**: Extract numeric literals to named constants in 'defines constant' block. - **Operator Keyword Shadowing** - **Error Code**: E11032 - **Rule**: Variable names that shadow operator keywords are banned - **Banned** - empty - length - contains - abs - sqrt - close - matches - **Fix**: Use descriptive alternatives: foundItems, isAvailable, searchPattern, textLength - **Note**: Complements bannedIdentifiers (E11031) which covers non-descriptive names like temp, data, flag. - **Discarded Return Values** - **Rules** - **Error Code**: E11050 - **Rule**: Discarded operator return is a compile error - **Example**: ```ek9 price + tax //ERROR: return value discarded ``` - **Error Code**: E11051 - **Rule**: Discarded pure method/function return is a compile error - **Example**: ```ek9 calculateTotal(items) //ERROR: return value discarded ``` - **Error Code**: E11052 - **Rule**: Discarded Result/Optional return silently swallows errors - **Example**: ```ek9 parseJSON(input) //ERROR: Result must be checked ``` - **Fix**: Always capture return values: total <- calculateTotal(items) - **Note**: Many languages allow ignoring return values. EK9 treats this as a bug. - **Tautological Expressions** - **Rules** - **Error Code**: E08080 - **Rule**: Self-assignment: x := x - **Error Code**: E08081 - **Rule**: Self-comparison: x == x - **Error Code**: E08082 - **Rule**: Constant comparison: 1 == 1 - **Error Code**: E08083 - **Rule**: Constant arithmetic: x - x, x / x - **Error Code**: E08084 - **Rule**: Redundant Boolean: b == true (use just b) - **Error Code**: E08085 - **Rule**: Logical tautology: a or not a - **Note**: AI copy-paste patterns frequently generate tautological expressions. - **Confusingly Similar Names** - **Error Code**: E11030 - **Rule**: Variables differing by 1-2 characters (Levenshtein distance) with same type are banned - **Examples** - data/dat - userId/usersId - count/counts - **Exemptions** - Different types - Constructor field shadowing - Loop counters - **Research**: 2.7x higher defect density (Butler et al., ICPC 2010) - **Class Design Anti Patterns** - **Rules** - **Error Code**: E11022 - **Rule**: Class with 4+ service fields and 0 data fields should be 'defines component' - **Error Code**: E11023 - **Rule**: 70%+ trait methods manually overridden — use 'by' delegation instead - **Error Code**: E11024 - **Rule**: 'by' delegation + 3+ services — split into class + component - **Error Code**: E11025 - **Rule**: 3+ services AND 3+ data fields — 'God Class', must decompose - **DI Injection Limits** - **Rules** - **Error Code**: E11040 - **Rule**: Max 4 injection fields per component - **Error Code**: E11041 - **Rule**: Similar dependencies should use a facade - **Error Code**: E11042 - **Rule**: Max 20 services per application registry - **Error Code**: E11043 - **Rule**: Max 5 injected types per method - **Fix**: Split large components into focused sub-components with facades. - **Data Clump Detection** - **Error Code**: E11053 - **Rule**: 3+ callables sharing 4+ parameters with matching types/names must extract to a record - **Fix**: Create a record containing the repeated parameters and pass the record instead. - **Type Traversal Limit** - **Error Code**: E11054 - **Rule**: Max 3 type transitions in a method chain (Law of Demeter) - **Example** - **Wrong**: ```ek9 city <- order.getCustomer().getAddress().getCity() ``` - **Correct**: ```ek9 city <- order.customerCity() ``` - **Fix**: Delegate through intermediate methods to limit coupling. - **Constant Organization** - **Rules** - **Error Code**: E11066 - **Rule**: Constants scattered across 3+ files in a module must be consolidated - **Error Code**: E11067 - **Rule**: 2+ files with 10+ total constants need a dedicated constants file - **Fix**: Use a single 'defines constant' block per module. - **Unused Capture Detection** - **Error Code**: E11018 - **Rule**: Dynamic function/class captures a variable but never uses it - **Fix**: Remove unused captures from the closure scope. - **Reference Ordering** - **Error Code**: E11026 - **Rule**: References (imports) must be sorted alphabetically - **Research**: 5-10% reduction in merge conflicts. ## Compilation - **Description**: EK9 compilation commands and flags - **Incremental** - **Compile**: ek9 -c main.ek9 - **Compile Debug**: ek9 -cg main.ek9 - **Compile Dev Debug**: ek9 -cd main.ek9 - **Full** - **Recompile**: ek9 -C main.ek9 - **Recompile Debug**: ek9 -Cg main.ek9 - **Recompile Dev Debug**: ek9 -Cd main.ek9 - **Clean All**: ek9 -Cl main.ek9 - **Run** - **Default**: ek9 main.ek9 - **Specific Program**: ek9 -r ProgramName main.ek9 - **Shebang**: ./main.ek9 - **Optimization** - **O0**: No optimization (forced with tests) - **O1**: Light optimization - **O2**: Standard optimization (incompatible with -t) - **O3**: Aggressive optimization (incompatible with -t) - **Error Levels** - **E0**: Minimal - single line - **E1**: Visual - Rust-style source display with caret markers - **E2**: Visual + 'Did you mean?' fuzzy matching suggestions - **E3**: Rich - full AI-oriented explanations with diagnosis, rationale, and examples - **E4**: Paradigm - concise summary of key design decisions (no break/continue/return, tri-state, closed types, automatic operators) - **E5**: Bootstrap - E4 paradigm summary plus complete forAI.json reference dump. Use once to learn EK9, then switch to E3 - **E6**: Local agent briefing - slim forAI-local.json operational reference for fine-tuned local LLMs in ek9 -ai. Contains role, Oracle, intents, protocol, terrain, and top reminders (~1900 tokens) - **Help** - **General Help**: ek9 -h - **Keyword Help**: ek9 -h - **Error Code Help**: ek9 -h Exxxx (e.g., ek9 -h E50001 — shows diagnosis, fix actions, examples for any error code) - **List All Keywords**: ek9 -H - **Question Answer**: ek9 -q (EK9-focused: answer + code example) - **Question By ID**: ek9 -q (fetch specific Q&A by ID, e.g., ek9 -q 45) - **Question Migration**: ek9 -qm (migration-focused: answer + language comparisons) - **List All Questions**: ek9 -q - **AI Note**: Use 'ek9 -q ' for EK9 syntax and examples. Use 'ek9 -q ' to follow cross-links (e.g., 'See Q45'). Use 'ek9 -qm ' when comparing EK9 to other languages. Use 'ek9 -h Exxxx' to diagnose compiler errors. - **DI Analysis** - **Compact**: ek9 -di main.ek9 (summary: registration counts, injection sites, health status) - **Rich**: ek9 -di -E3 main.ek9 (full wiring map, injection sites, aspect chains, structured guidance) - **AI Note**: Run 'ek9 -di -E3' BEFORE modifying DI wiring to understand current registrations, ordering, and injection field counts - **Target**: ek9 -T java main.ek9 (Java is only target currently) - **Verbose**: ek9 -v -c main.ek9 - **AI Recommendation**: Use -E5 once to bootstrap, then -E3 for ongoing development. Use 'ek9 -h Exxxx' to diagnose any error. Use 'ek9 -di -E3' before modifying DI wiring. Use 'ek9 -q ' for EK9 syntax/examples, 'ek9 -qm ' for language comparisons. - **Documentation**: https://ek9.io/commandline.html ## Testing - **Description**: EK9 testing commands, output formats, and coverage - **Commands** - **Run Tests**: ek9 -t main.ek9 - **List Tests**: ek9 -tL main.ek9 - **Run Group**: ek9 -tg groupname main.ek9 - **Show Coverage**: ek9 -tC main.ek9 - **Debug**: ek9 -d port main.ek9 - **Output Formats** - **T0**: Terse - for scripting and CI pipelines - **T1**: Human-readable (default with -t) - **T2**: JSON - programmatic analysis and AI tool integration - **T3**: JUnit XML - CI/CD integration - **T4**: Detailed coverage report - writes .ek9/coverage-detail.json with file:line for uncovered methods/branches - **T5**: Verbose coverage - lists BOTH covered AND uncovered items (for verification and debugging) - **T6**: Interactive HTML coverage report - writes .ek9/coverage/index.html with SVG charts, module breakdown, source views, dark/light theme, search, mobile-responsive - **Test Discovery**: Programs marked with @Test in /dev directory and subdirectories are auto-discovered - **Test Groups**: @Test: "groupname" syntax allows selective test execution with -tg - **Coverage** - **Threshold**: 80% - automatic detailed report to .ek9/coverage-detail.json when below - **View Results**: ek9 -tC main.ek9 - **Constraint**: -t cannot be combined with -O2 or -O3 optimization flags - **AI Recommendation**: Use -t2 for JSON output in automated workflows, -t for human-readable during development - **Documentation**: https://ek9.io/commandline.html#testing ## Packaging - **Description**: EK9 dependency management, versioning, packaging, and deployment - **Dependencies** - **Resolve**: ek9 -Dp main.ek9 - **Repository**: repo.ek9lang.org - **Local Cache**: $HOME/.ek9/lib - **Note**: Triggers clean before resolving; dependencies declared in package construct - **Versioning** - **Print**: ek9 -PV main.ek9 - **Increment**: ek9 -IV major|minor|patch|build main.ek9 - **Set**: ek9 -SV major.minor.patch main.ek9 - **Set Feature**: ek9 -SF major.minor.patch-feature main.ek9 - **Format**: major.minor.patch-build (e.g., 6.8.1-0) or major.minor.patch-feature-build (e.g., 6.8.0-specials-0) - **Package**: ek9 -P main.ek9 - **Install Local**: ek9 -I main.ek9 - **Deploy** - **Command**: ek9 -D main.ek9 - **Note**: Triggers -P and -Gk automatically - **Generate Keys**: ek9 -Gk - **Config File**: $HOME/.ek9/config - **Credentials File**: $HOME/.ek9/credentials - **AI Recommendation**: Use -IV patch for bug fixes, -IV minor for new features, -IV major for breaking changes - **Documentation**: https://ek9.io/commandline.html#packaging ## Project Structure - **Description**: EK9 project directory structure and conventions - **Layout** - **Main.ek9**: Main source file with package construct defining module, version, and dependencies - **.ek9/**: Generated build artifacts directory (auto-created by compiler) - **Dev/**: Development code — test programs (@Test), examples, templates. Not included in library packaging - **$HOME/.ek9/lib/**: Global dependency cache — resolved libraries stored here - **$HOME/.ek9/config**: Artifact server settings for deployment - **$HOME/.ek9/credentials**: Authentication details for artifact server - **Package Construct** - **Description**: The package construct in main source declares project metadata and dependencies - **Example**: ```ek9 defines package version <- 1.0.0-0 description <- "My EK9 library" deps org.example.lib 2.0.0-0 ``` - **Dev Directory** - **Description**: Programs in /dev are auto-discovered for testing - **Convention**: Test files use @Test annotation, groups use @Test: "groupname" - **Excluded**: Dev code is not included when the package is consumed as a library - **Documentation**: https://ek9.io/packaging.html ## Language Server - **Description**: EK9 Language Server Protocol (LSP) integration - **Start**: ek9 -ls main.ek9 - **Start With Hover**: ek9 -lsh main.ek9 - **Protocol**: stdin/stdout LSP protocol - **Vscode Extension**: Available for VSCode with syntax highlighting and real-time error reporting - **Hover Help**: -lsh enables hover documentation for operators and symbols - **Documentation**: https://ek9.io/commandline.html#languageserver ## REPL - **Description**: EK9 interactive Read-Eval-Print Loop - **Start**: ek9 -repl - **Start With Suggestions**: ek9 -repl -E2 - **Modes** - /function - /class - /record - /trait - /type - **Commands** - **Edit**: :edit — opens $VISUAL or $EDITOR for multi-line editing - **Edit Line**: :edit N — edit line N in buffer - **Delete**: :del N — delete line N - **Insert**: :ins N — insert before line N - **Undo**: :undo — undo last change - **Save**: :save — save session - **Load**: :load — load previous session - **Hover Help**: Ctrl+H shows symbol information in REPL - **Documentation**: https://ek9.io/commandline.html#repl ## Environment - **Description**: EK9 environment variables and runtime configuration - **Variables** - **EK9_COMPILER_MEMORY**: Compiler heap size (default: -Xmx512m), e.g., export EK9_COMPILER_MEMORY="-Xmx1024m" - **EK9_APPLICATION_MEMORY**: Application heap size (default: -Xmx512m), e.g., export EK9_APPLICATION_MEMORY="-Xmx2048m" - **EK9_TARGET**: Target architecture (default: java) - **EK9_PLAIN_TEXT**: Force plain text error output (no colors, no unicode) - **Stdin Stdout** - **Description**: EK9 programs integrate with Unix pipes and redirection - **Examples** - echo "Hello" | ek9 program.ek9 - ek9 filter.ek9 < input.txt > output.txt - cat data.txt | ek9 filter.ek9 | sort | uniq - **Classes**: Stdin(), Stdout(), Stderr() - **Set Env Flag**: ek9 -e name=value main.ek9 - **Documentation**: https://ek9.io/commandline.html ## Exit Codes - **Description**: EK9 process exit codes for CI/CD integration and scripting. TWO LAYERS: the 'ek9' native wrapper (normal usage) follows Unix convention — 0 means success. The underlying compiler jar (java -jar ek9c-jar-with-dependencies.jar) speaks an internal protocol the wrapper translates; callers invoking the jar DIRECTLY (AI agents, scripts) must read jarProtocol, NOT the wrapper view. - **Jar Protocol** - **0**: A run-command was printed to stdout — the caller must exec it; the program's own exit code then passes through untouched - **1**: SUCCESS with nothing to run (e.g. -c/-C compile-only, -V, -Gk) — silent by Unix convention; the wrapper maps this to 0. Direct-jar callers: exit 1 + no output = the operation WORKED - **2+**: Error conditions — identical to the wrapper view below - **Codes** - **0**: Success (wrapper view; jar: run-command printed — see jarProtocol) - **1**: Program's own exit code / wrapper exec failure (jar: success-nothing-to-run) - **2**: Invalid parameters - **3**: File access error - **4**: Invalid parameter combination - **5**: No programs found in source - **6**: Multiple programs exist — use -r to specify - **7**: Language Server failed to start - **8**: Compilation failed - **9**: Wrong number of program arguments - **10**: Argument type conversion failed - **11**: Test execution failed - **12**: Tests pass but coverage below required threshold - **13**: MCP server failed to start - **Ci Example**: ek9 -C main.ek9 && ek9 -t2 main.ek9 > test-results.json && ek9 -D main.ek9 - **Documentation**: https://ek9.io/commandline.html ## Development Workflow - **Description**: Complete EK9 development lifecycle from creation to deployment - **Steps** - **Phase**: create - **Description**: Create source file with package construct defining module, version, and dependencies - **Command**: touch myapp.ek9 - **Phase**: develop - **Description**: Iterative development with incremental compilation - **Command**: ek9 -c myapp.ek9 - **Phase**: diAnalysis - **Description**: Inspect DI wiring before modifying injection (only if using components/applications) - **Command**: ek9 -di -E3 myapp.ek9 - **Phase**: errorDiagnosis - **Description**: Look up any compiler error for full diagnosis and fix actions - **Command**: ek9 -h Exxxx - **Phase**: run - **Description**: Compile and execute the program - **Command**: ek9 myapp.ek9 - **Phase**: test - **Description**: Run all test programs in /dev directory - **Command**: ek9 -t myapp.ek9 - **Phase**: coverage - **Description**: Check test coverage meets 80% threshold - **Command**: ek9 -tC myapp.ek9 - **Phase**: resolveDeps - **Description**: Resolve dependencies from repo.ek9lang.org - **Command**: ek9 -Dp myapp.ek9 - **Phase**: version - **Description**: Increment version before release - **Command**: ek9 -IV minor myapp.ek9 - **Phase**: package - **Description**: Create deployment package - **Command**: ek9 -P myapp.ek9 - **Phase**: installLocal - **Description**: Install to local library for use by other projects - **Command**: ek9 -I myapp.ek9 - **Phase**: deploy - **Description**: Deploy to artifact server (generates keys if needed) - **Command**: ek9 -D myapp.ek9 - **Documentation**: https://ek9.io/commandline.html ## Stream Pipelines - **Description**: EK9's replacement for loops needing break/continue. NOTE: for/while/do-while loops still exist for iteration with mutation, side effects, and I/O - **Sources** - **Name**: cat - **Purpose**: Create stream from collection, iterator, or enumeration - **Name**: for - **Purpose**: Range-based source: for i in 1 .. 10 - **Intermediate Operations** - **Name**: filter - **Purpose**: Keep items matching predicate (opposite of reject) - **Name**: reject - **Purpose**: Remove items matching predicate (opposite of filter) - **Name**: map - **Purpose**: Transform each item to a different type - **Name**: sort - **Purpose**: Order items using comparison function - **Name**: group - **Purpose**: Group into Lists by key function - **Name**: split - **Purpose**: Partition into sub-groups by criteria - **Name**: join - **Purpose**: Combine two items into one - **Name**: uniq - **Purpose**: Remove duplicates (optionally: uniq by key) - **Name**: flatten - **Purpose**: Expand nested collections: List of List -> individual items - **Name**: head - **Purpose**: Take first N items (replaces break) - **Name**: tail - **Purpose**: Take last N items - **Name**: skip - **Purpose**: Discard first N items (replaces continue with counter) - **Name**: tee - **Purpose**: Side-copy into variable, continue pipeline - **Name**: call - **Purpose**: Invoke function delegate on each item - **Name**: async - **Purpose**: Invoke function delegate asynchronously - **Terminals** - **Name**: > sink - **Purpose**: Direct output to collection or Stdout - **Name**: >> collection - **Purpose**: Append to existing collection - **Name**: collect as Type - **Purpose**: Materialize into new collection - **Replaces Imperative Patterns** - **Break**: head N (stop after N items) - **Continue**: filter by condition (skip non-matching) - **Loop With Set**: uniq (deduplicate without manual tracking) - **Nested Loops**: group by key | map with transform | flatten - **Examples** - **Description**: Find first match (replaces loop + break) - **Code**: ```ek9 result <- cat items | filter by matches | head ``` - **Description**: Skip invalid items (replaces continue) - **Code**: ```ek9 cat items | filter by isValid | map with transform | collect as List of Output ``` - **Description**: Sort, group, and process - **Code**: ```ek9 cat library | sort by comparingAuthor | group by authorId | filter with sufficientBooks | map by orderOnPublishedDate | flatten > stdout ``` - **Description**: Collect all enum values - **Code**: ```ek9 allColours <- cat Colour | collect as List of Colour ``` - **Documentation**: https://ek9.io/streamsAndPipelines.html ## Purity Rules - **Description**: EK9's pragmatic purity model - compile-time enforcement limiting internal state mutation - **Syntax**: functionName() as pure - **Clarification** - **Not Haskell Purity**: EK9 'pure' controls INTERNAL STATE MUTATION, not all side effects like Haskell - **Io Is Separate**: I/O is essential and handled via IO types - not what 'pure' restricts - **What Pure Prevents**: Unexpected internal state changes that cause bugs, race conditions, unpredictable behavior - **Why Println Works In Pure**: println doesn't mutate program state - it's an external I/O operation, a different concern - **What Pure Enforces** - **Can Only Call Pure Functions**: Pure functions/methods can only call other pure functions/methods (transitive) - **No Direct Reassignment**: Cannot use := or : to reassign variables - only :=? (assign if unset) allowed - **Cannot Modify Parameters**: Cannot reassign or mutate incoming parameters (even :=? not allowed) - **Cannot Mutate Fields**: Cannot modify instance fields/properties (except in constructors) - **Can Read Fields**: Can read fields and call pure methods on them - **Exceptions** - **Return Variable**: Return variable (<- rtn) can be assigned multiple times - **Unset Variables**: Variables that are UNSET can be assigned with :=? - **Constructors**: Constructors can initialize fields with :=? (they MUST initialize state) - **Loop Variables**: System-managed loop variables (for i in ...) are allowed - **Constructor Purity** - **Rule**: All constructors in a class must have CONSISTENT purity (all pure or all non-pure) - **Error Code**: E50402 - **Pattern**: Use :=? to initialize properties in pure constructors - **Error Codes** - **E50400**: NONE_PURE_CALL_IN_PURE_SCOPE - calling non-pure from pure context - **E50401**: NO_PURE_REASSIGNMENT - direct := assignment in pure context - **E50402**: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS - inconsistent constructor purity - **E50403**: SUPER_IS_PURE - non-pure function extending pure abstract - **E50404**: SUPER_IS_NOT_PURE - pure function extending non-pure abstract - **E50405**: NO_INCOMING_ARGUMENT_REASSIGNMENT - modifying incoming parameters - **Practical Guidance** - **When To Use Pure**: Mark pure if it doesn't mutate any field/property state - **Benefit**: Pure functions are deterministic, testable, and safe for concurrent execution - **Pattern**: Use more local variables (like LLVM SSA) instead of reassignment - **Subtlety** - **Creating Vs Calling**: Creating a non-pure function in pure context is OK - but CALLING it is NOT - **Example**: ```ek9 rtn :=? () is SomeNonPureFunction as function ... // OK - creation is pure result <- rtn(22) // ERROR - calling non-pure function ``` - **Documentation**: https://ek9.io/basics.html#pure ## Sanitized Types - **Description**: EK9's compile-time security model for input validation - 'Rejection at the Source' - **Critical Rule**: The 'sanitized' modifier is ONLY valid on String parameters in function/method signatures - **Philosophy** - **Name**: Rejection at the Source - **Meaning**: Sanitize external inputs at system entry points ONLY - once inside trusted boundary, data is clean - **Benefit**: Security happens at compile-time through parameter declaration, not runtime checks scattered through code - **Parameter Semantics** - **Critical Insight**: Sanitized parameters are effectively PASS-BY-VALUE, not pass-by-reference - **Explanation**: EK9 normally uses pass-by-reference (changes to parameter affect original). For sanitized parameters, a NEW copy is created (if safe) or an UNSET value is returned (if threat detected). The original is NEVER exposed. - **Why No Aliasing**: Because we always create a copy, aliasing the parameter would expose a reference that doesn't represent the sanitization semantics. Direct assignment is blocked to highlight this pass-by-value behavior. - **Where Allowed** - **Allowed** - Function parameters - Method parameters - **Not Allowed** - Variables declarations - Return types - Capture variables - Call sites - **Threat Types** - **Name**: SQL_INJECTION - **Severity**: 8 - **Description**: SQL injection patterns - **Name**: SQL_INJECTION_SIGNATURE - **Severity**: 8 - **Description**: libinjection-style SQL detection - **Name**: COMMAND_INJECTION - **Severity**: 9 - **Description**: Shell/command injection - **Name**: PATH_TRAVERSAL - **Severity**: 6 - **Description**: Directory traversal (../) - **Name**: XSS - **Severity**: 7 - **Description**: Cross-site scripting - **Name**: XXE - **Severity**: 8 - **Description**: XML External Entity - **Name**: SSTI - **Severity**: 7 - **Description**: Server-Side Template Injection - **Logging Formats** - **Description**: Controlled by EK9_SANITIZER_LOG_FORMAT environment variable - **Formats** - **Name**: JSON - **Default**: true - **Description**: Universal JSON format for any log shipper - **Name**: ECS - **Description**: Elastic Common Schema - for Elasticsearch, Splunk, Datadog - **Name**: CEF - **Description**: Common Event Format - for ArcSight, Azure Sentinel, QRadar - **Name**: SIMPLE - **Description**: Simple bracket format for testing/scripts - **Name**: SILENT - **Description**: Suppresses all output - for testing - **Input Sanitizer Class** - **Description**: Programmatic access to sanitization for manual validation - **Methods** - **Name**: sanitize(input) - **Returns**: Safe input or UNSET if threat - **Name**: isSafe(input) - **Returns**: Boolean true/false/unset - **Name**: detectThreat(input) - **Returns**: Threat type string or UNSET if safe - **Name**: sanitizeWithoutPathChecks(input) - **Returns**: For environment variables with legitimate paths - **Customization**: Cannot be directly modified. Companies must extend InputSanitizer and call methods manually. - **Examples** - **Correct** - **Function Parameter**: ```ek9 processInput() -> sanitized input as String if input? // Safe to use - guaranteed clean db.query(input) ``` - **Manual Sanitization**: ```ek9 sanitizer <- InputSanitizer() if clean <- sanitizer.sanitize(userInput) process(clean) ``` - **Wrong** - **Direct Assignment**: local <- sanitizedParam // ERROR: Cannot alias sanitized parameter - **Variable Declaration**: sanitized name <- getString() // ERROR: Not on variables - **Capture Variable**: sanitized captured as String // ERROR: Not in captures - **Call Site**: process(sanitized value) // ERROR: Not at call site - **Error Codes** - **E07910**: SANITIZED_ON_ARGUMENT_NOT_SUPPORTED - sanitized used at call site - **E07920**: SANITIZED_ONLY_ON_STRING - sanitized on non-String type - **E07930**: CANNOT_ASSIGN_FROM_SANITIZED - direct assignment from sanitized parameter (aliasing) - **E07940**: SANITIZED_ONLY_IN_DECLARATION - sanitized in wrong context - **E07941**: SANITIZED_NOT_ALLOWED_IN_CAPTURE - sanitized on captured variable - **E07942**: SANITIZED_NOT_ALLOWED_ON_VARIABLE - sanitized on variable declaration - **E07943**: SANITIZED_NOT_ALLOWED_ON_RETURN - sanitized on return type - **Documentation**: https://ek9.io/sanitized.html ## Method Resolution - **Description**: EK9's cost-based method matching algorithm for overloaded methods - **Critical Rule**: Methods are overloadable, functions are NOT - function names must be unique per module - **Cost Based Matching** - **Description**: Lower cost = better match. Percentage = 100.0 - totalCost - **Costs** - **Name**: ZERO_COST - **Value**: 0.0 - **Meaning**: Exact type match - perfect - **Name**: SUPER_COST - **Value**: 0.05 - **Meaning**: Match via superclass (per level) - **Name**: TRAIT_COST - **Value**: 0.1 - **Meaning**: Match via trait (less specific than superclass) - **Name**: COERCION_COST - **Value**: 0.5 - **Meaning**: Type promotion via #^ operator required - **Name**: HIGH_COST - **Value**: 20.0 - **Meaning**: Match to 'Any' type - last resort only - **Name**: INVALID_COST - **Value**: -1.0 - **Meaning**: No match possible - **Why Trait Costs More Than Super**: A class can implement many traits but only extends one superclass - superclass relationship is more specific - **Ambiguity Detection** - **Description**: Two methods are ambiguous if their percentage match is within 0.001 tolerance - **Why Small Tolerance**: Cost calculations use multipliers for multiple parameters, creating finer granularity - **Error Code**: E06140 - **Example** - **Methods**: methodA(C1, C2) and methodA(C2, C1) - **Call**: methodA(c2, c3) where c3 extends C2 extends C1 - **Cost A**: c2→C1 (0.05) + c3→C2 (0.05) = 0.10 - **Cost B**: c2→C2 (0.0) + c3→C1 (0.10) = 0.10 - **Result**: AMBIGUOUS - both have identical cost - **Any Type Handling** - **Description**: 'Any' is the universal base type - root of EK9's type hierarchy - **Cost**: HIGH_COST (20.0) - deliberately expensive to ensure specific types always preferred - **Pattern**: Many built-in types have both specific and Any overloads - **Example**: ```ek9 _eq(List arg) costs 0.0, _eq(Any arg) costs 20.0 - List version always wins ``` - **Return Type Matching** - **Description**: Return type compatibility is pass/fail only - no cost impact - **Rule**: If return type specified in search and incompatible, method is rejected (not cost-adjusted) - **No Automatic Promotions** - **Critical Rule**: EK9 has NO hidden/automatic type promotions - **Explanation**: Everything is an object. Integer has explicit #^ promotion operator to Float - **Benefit**: No surprises - all type conversions are visible in the code - **Function Resolution** - **Description**: Functions CANNOT be overloaded - names must be unique per module - **Error If Duplicate**: E02010 (DUPLICATE_NAME) - **Resolution**: Simple parameter signature validation - no cost-based matching needed - **Error Codes** - **E06140**: METHOD_AMBIGUOUS - multiple methods match with same cost - **E50060**: METHOD_NOT_RESOLVED - no matching method found - **E06270**: FUNCTION_PARAMETER_MISMATCH - function called with wrong parameters - **Documentation**: https://ek9.io/methods.html ## Operator Semantics - **Description**: EK9 operator behavior - mutating vs non-mutating - **Critical Warning**: Mutating operators return 'this' - assignment creates ALIASES, not copies - **Mutating Operators** - **Description**: These operators MUTATE the target and return reference to same object - **Operators** - **Symbol**: ++ - **Method**: _inc - **Note**: x++ mutates x AND returns x (not a copy!) - **Symbol**: -- - **Method**: _dec - **Note**: x-- mutates x AND returns x - **Symbol**: += - **Method**: _addAss - **Note**: Mutates and returns this - **Symbol**: -= - **Method**: _subAss - **Note**: Mutates and returns this - **Symbol**: *= - **Method**: _mulAss - **Note**: Mutates and returns this - **Symbol**: /= - **Method**: _divAss - **Note**: Mutates and returns this - **Symbol**: :=: - **Method**: _copy - **Note**: Deep copy INTO target - **Symbol**: :^: - **Method**: _replace - **Note**: Replace content - **Symbol**: :~: - **Method**: _merge - **Note**: Merge content - **Aliasing Danger** - **Example**: ```ek9 x <- Integer(5) y <- x++ // y and x are NOW THE SAME OBJECT! // x = 6, y = 6 (both reference same Integer) ``` - **Warning**: This is DIFFERENT from C/C++/Java where y = x++ creates independent copies - **Non Mutating Operators** - **Description**: These operators return NEW objects - safe for functional programming - **Operators** - **Symbol**: + - **Method**: _add - **Symbol**: - - **Method**: _sub - **Symbol**: * - **Method**: _mul - **Symbol**: / - **Method**: _div - **Symbol**: - (unary) - **Method**: _negate - **Symbol**: abs - **Method**: _abs - **Symbol**: == - **Method**: _eq - **Symbol**: <> - **Method**: _neq - **Symbol**: < - **Method**: _lt - **Symbol**: > - **Method**: _gt - **Symbol**: <= - **Method**: _le - **Symbol**: >= - **Method**: _ge - **Symbol**: <=> - **Method**: _cmp - **Documentation**: https://ek9.io/operators.html ## Coalescing Operators - **Description**: Operators that gracefully handle tri-state (absent/unset/set) - **Operators** - **Symbol**: ?? - **Name**: Null Coalescing - **Meaning**: Return left if SET, otherwise return right - **Example**: ```ek9 result <- getValue() ?? defaultValue ``` - **Symbol**: ?: - **Name**: Elvis Operator - **Meaning**: Return left if SET, otherwise return right (same as ??) - **Example**: ```ek9 name <- input ?: "Anonymous" ``` - **Symbol**: :=? - **Name**: Guarded Assignment - **Meaning**: Only assign if target is currently UNSET - **Example**: ```ek9 config :=? loadDefaults() // Only loads if config unset ``` - **Symbol**: ? - **Name**: Is Set Check - **Meaning**: Returns true if value is SET (has meaningful value) - **Example**: ```ek9 if name? process(name) ``` - **Documentation**: https://ek9.io/operators.html#coalescing ## Require And Assert - **Description**: EK9 precondition and assertion mechanisms - **Require** - **Purpose**: Verify precondition — throws UNCATCHABLE exception if false (like panic) - **Syntax**: require expression - **Examples** - require name? - require age > 0 - require firstName? and lastName? - require not true - **Semantics**: When require fails, it indicates a serious unrecoverable defect. There is NO recovery — execution terminates. By design, require failures cannot drive control flow. - **Use Cases** - Constructor argument validation: require id? - Method preconditions: require amount > 0 - Invariant checking: require balance >= 0 - Defensive default constructor blocking: require not true - **Assert** - **Purpose**: Test assertions in @Test programs — throws exception if false - **Syntax**: assert expression - **Note**: Use in @Test programs for test validation. Similar to require but specifically for test assertions. - **Documentation**: https://ek9.io/testing.html#require ## Syntax - **Indentation**: 2 spaces (not tabs) - **No Semicolons**: true - **No Curly Braces**: true - **Declaration**: name <- value - **Typed Declaration**: name as Type: value - **Assignment**: name: value - **Guard Assignment**: name :=? value - **Comments** - **Single Line**: // comment - **Multi Line**: ## Common AI Mistakes - **Description**: Frequent mistakes AI assistants make when generating EK9 code - **Syntax Mistakes** - **Multiple Arrows** - **Description**: Using multiple -> for multiple parameters - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 myFunc() -> arg1 as String -> arg2 as Integer ``` - **Correct**: ```ek9 myFunc() -> arg1 as String arg2 as Integer ``` - **Rule**: Single -> on its own line, then each parameter indented below - **Curly Braces For Blocks** - **Description**: Using {} for code blocks like C/Java - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 if condition { doSomething() } ``` - **Correct**: ```ek9 if condition doSomething() ``` - **Rule**: Curly braces {} are ONLY for Dict literals: {1: "one", 2: "two"} - **Square Brackets As Arrays** - **Description**: Thinking [] creates arrays - **Severity**: MISCONCEPTION - **Correct**: ```ek9 [1, 2, 3] creates a List of Integer, NOT an array ``` - **Rule**: EK9 has no arrays - [] is shorthand for List literals - **Python Colon After Statements** - **Description**: Adding : after if/for/while like Python - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 if condition: ``` - **Correct**: ```ek9 if condition ``` - **Rule**: No colon after control flow statements - just newline and indent - **Lambda Syntax** - **Description**: Using lambda/arrow function syntax from other languages - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 fn = (x) => x * 2 ``` - **Correct**: ```ek9 fn <- (x) is Transformer as function <- rtn: x * 2 ``` - **Rule**: EK9 uses 'dynamic functions': (captures) is FunctionType as function - **C Style For Loop** - **Description**: Using C-style for(i=0; i<10; i++) - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 for (i = 0; i < 10; i++) ``` - **Correct**: ```ek9 for i in 0 ... 10 ``` - **Rule**: Use 'for variable in range' or 'for item in collection' - **Enum Values On One Line** - **Description**: Enum values must be one-per-line OR comma-separated — not indented block without commas - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 CardSuit Hearts Diamonds ``` - **Correct**: ```ek9 CardSuit Hearts Diamonds // OR comma-separated: CardSuit Hearts, Diamonds, Clubs, Spades ``` - **Rule**: Each enum value on its own line at one indent level deeper than the type name, OR comma-separated on one/multiple lines - **Cat With Range** - **Description**: Using 'cat' with a numeric range — 'cat' takes collections or types, not ranges - **Severity**: PARSE_FAILURE - **Wrong**: ```ek9 cat 1 ... 10 | map by transform | collect as List of Integer ``` - **Correct**: ```ek9 for i in 1 ... 10 | map by transform | collect as List of Integer ``` - **Rule**: 'cat' streams from collections (cat myList) or enumeration types (cat Colour). Use 'for i in start ... end' to generate ranges in stream pipelines. - **Aggregate Construction Rules** - **Description**: Mandatory rules for declaring records / classes / components — most AI-generated test fixtures violate one of these on the first try. Each rule is enforced by the EK9 compiler with a specific error code. - **Field Initialisation Required** - **Description**: Every property/field on a record / class / component must be initialised at declaration OR through a developer-coded constructor - **Error Codes** - E08070 - E07170 - E08180 - **Rule**: Fields of form 'name as Type' must have an initialiser: 'name as Type: defaultExpression'. The `?` marker syntax 'name as Type?' indicates 'allowed unset', NOT 'no default needed' — it still requires the field to be set via constructor. - **Wrong**: ```ek9 defines record Point x as Float y as Float ``` - **Correct**: ```ek9 defines record Point x as Float: 0.0 y as Float: 0.0 ``` - **Alternative Correct**: ```ek9 defines record Point x as Float y as Float Point() -> xArg as Float yArg as Float this.x: xArg this.y: yArg ``` - **Is Set Operator Required** - **Description**: Aggregates (record / class / component) with declared fields MUST provide the '?' (isSet) operator — required by EK9's tri-state semantics. Applies equally to records AND classes — it is NOT a record-only rule. - **Error Codes** - E07200 - E07235 - **Rule**: Either use 'default operator ?' (compiler synthesises from field-? AND of all fields) or 'override operator ? as pure' with custom body. Records can additionally use bare 'default operator' which synthesises ? plus every other defaultable operator at once. Classes can mix 'default operator ?' with custom operators in any order subject to the member-ordering rule. - **Wrong**: ```ek9 defines class Greeter name as String: "hi" greet() as pure <- result as String: name ``` - **Correct**: ```ek9 defines class Greeter name as String: "hi" greet() as pure <- result as String: name default operator ? ``` - **Member Ordering Fields Methods Operators** - **Description**: EK9 enforces a strict body-member ordering inside classes / records / components: ALL fields first, THEN methods, THEN operators. Mixing the three categories or putting methods before fields is a parse-level error. - **Error Code**: E01086 - **Rule**: Body order: fields → methods → operators. Within each band the order is flexible; between bands the rule is absolute. - **Wrong**: ```ek9 defines class Greeter default operator ? name as String: "hi" greet() as pure <- result as String: name ``` - **Correct**: ```ek9 defines class Greeter name as String: "hi" greet() as pure <- result as String: name default operator ? ``` - **Minimal Compiling Aggregate Template** - **Description**: The smallest aggregate fixture that compiles cleanly under the EK9 quality gate. Use as the starting template when writing test fixtures or example code. - **Record**: ```ek9 #!ek9 defines module com.example.geo defines record Point x as Float: 0.0 y as Float: 0.0 default operator //EOF ``` - **Class**: ```ek9 #!ek9 defines module com.example.greet defines class Greeter greeting as String: "Hello" greet() as pure <- result as String: greeting default operator ? //EOF ``` - **Note**: Records can use bare 'default operator' to get all defaults at once; classes typically need at least 'default operator ?'. Both must satisfy fieldInitialisationRequired and memberOrderingFieldsMethodsOperators. - **Semantic Mistakes** - **Using Excluded Keywords** - **Description**: Using break, continue, return, null - **Error Codes** - E01070 - E01071 - E01072 - E01073 - **Rule**: These keywords do not exist - see $.excludedFeatures - **Instanceof Or Casting** - **Description**: Trying to check or cast types at runtime - **Rule**: No instanceof, no casting - use dispatcher pattern only - **Automatic Type Promotion** - **Description**: Assuming Integer converts to Float automatically - **Rule**: All promotions explicit via #^ operator: intVal#^ - **Overloading Functions** - **Description**: Creating multiple functions with same name - **Error Code**: E01040 - **Rule**: Functions cannot be overloaded - only methods can - **Extending Closed Types** - **Description**: Extending List, Dict, Optional, etc. - **Error Code**: E05030 - **Rule**: Built-in generic types are closed - use composition - **Reassigning In Pure** - **Description**: Using := in pure functions - **Error Code**: E50401 - **Rule**: Only :=? (assign if unset) allowed in pure contexts - **Aliasing Sanitized** - **Description**: Direct assignment from sanitized parameter - **Error Code**: E07930 - **Rule**: Cannot alias sanitized params - pass-by-value semantics - **Increment In Expression** - **Description**: Using x++ in expressions like y <- x++ - **Error Code**: E07950 - **Rule**: ++/-- are statement-only, not expression operators - **Manual JSON Serialization** - **Description**: Writing custom JSON conversion instead of using default operator $$ - **Rule**: Use 'default operator $$' on records/classes for auto-generated JSON. List of T also has $$ operator. - **Example** - **Wrong**: ```ek9 toJSON() -> c as Customer <- rtn as String: `{ "name": "${c.name}" }` ``` - **Correct**: ```ek9 Customer name as String default operator $$ //Usage: json <- $$customer ``` - **Manual String Serialization** - **Description**: Writing custom String conversion instead of using default operator $ - **Rule**: Use 'default operator $' for auto-generated string representation - **Example** - **Wrong**: ```ek9 toString() -> c as Customer <- rtn as String: c.name + " " + c.surname ``` - **Correct**: ```ek9 Customer name as String surname as String default operator $ //Usage: str <- $customer ``` - **Unused Trait Definition** - **Description**: Defining a trait but implementing its operations as standalone functions instead of a class - **Rule**: If you define a trait, always implement it with 'class X with trait of TraitName'. Never define a trait and then ignore it. - **Severity**: DESIGN_FLAW - **Unused Parameter** - **Description**: Declaring a parameter that is never referenced in the function/method body - **Error Code**: E08091 - **Rule**: Every declared parameter must be used. Remove unused parameters or use them in the body. - **Exemptions** - Abstract methods/functions (no body) - Override methods (signature forced by parent) - Dispatcher methods (must accept dispatched type) - Operators (fixed semantics) - Functions explicitly extending another function - Methods on parameterised types (signature forced by generic contract) - **Severity**: COMPILE_ERROR - **Operator Question Must Be Pure** - **Description**: operator ? must ALWAYS be declared 'as pure' — it is a query, never mutates - **Error Code**: E07500 - **Wrong**: ```ek9 override operator ? <- rtn as Boolean: name? ``` - **Correct**: ```ek9 override operator ? as pure <- rtn as Boolean: name? ``` - **Rule**: operator ? checks if a value is set — this is inherently pure. The compiler requires 'as pure' on every ? operator declaration. - **Severity**: COMPILE_ERROR - **Enum Switch Exhaustiveness And Default** - **Description**: Switch expression on enum requires BOTH all enum cases AND a default clause - **Error Codes** - E07310 - E07330 - **Wrong**: ```ek9 switch status <- rtn as String? case Status.Active rtn: "active" default rtn: "other" ``` - **Correct**: ```ek9 switch status <- rtn as String? case Status.Active rtn: "active" case Status.Inactive rtn: "inactive" default rtn: "unknown" ``` - **Rule**: When switching on an enumeration as an EXPRESSION (<- rtn), list ALL enum values explicitly AND include a default. E07310 fires if cases are missing, E07330 fires if default is missing. - **Severity**: COMPILE_ERROR - **Copy Constructor In Pure Context** - **Description**: Calling a copy constructor like TypeName(existingVar) inside a pure method when the constructor is not pure - **Error Code**: E08130 - **Wrong**: ```ek9 status() as pure <- rtn as ConnectionStatus: ConnectionStatus(currentStatus) ``` - **Correct**: ```ek9 status() <- rtn as ConnectionStatus: ConnectionStatus(currentStatus) // OR if method must be pure, return the field directly if semantics allow it ``` - **Rule**: Copy constructors may not be marked pure. If you need to return a copy from a pure method, either remove 'as pure' from the method, or find a pure alternative. Use 'ek9 -h ' to check if the copy constructor is pure. - **Severity**: COMPILE_ERROR - **Assignment Confusion** - **Description**: EK9 has multiple assignment forms - **Forms** - **Syntax**: name <- value - **Meaning**: Declaration with type INFERENCE (most common) - **Syntax**: name as Type: value - **Meaning**: Declaration with EXPLICIT type, assign with : - **Syntax**: name as Type := value - **Meaning**: Declaration with EXPLICIT type, assign with := - **Syntax**: name: value - **Meaning**: REASSIGNMENT to existing variable - **Syntax**: name := value - **Meaning**: REASSIGNMENT to existing variable - **Syntax**: name :=? value - **Meaning**: Assign ONLY if currently unset - **Key Point**: <- is NOT the only way to declare - it's just the type-inference form ## Error Code Index - **Description**: Lookup from error code to forAI.json path — AI assistants use this to find structured guidance for any compiler error - **E01070**: excludedFeatures.break - **E01071**: excludedFeatures.continue - **E01072**: excludedFeatures.return - **E01073**: excludedFeatures.null - **E01074**: excludedFeatures.semicolon - **E01075**: excludedFeatures.new - **E01076**: excludedFeatures.goto - **E01077**: excludedFeatures.def - **E01078**: excludedFeatures.elif - **E01079**: excludedFeatures.None - **E01080**: excludedFeatures.self - **E01081**: excludedFeatures.listComprehension - **E01082**: excludedFeatures.multipleArgumentArrows - **E01083**: excludedFeatures.multipleReturnArrows - **E05030**: closedByDefault - **E11010**: qualityMetrics.cyclomaticComplexity - **E11021**: qualityMetrics.cognitiveComplexity - **E11011**: qualityMetrics.nestingDepth - **E11012**: qualityMetrics.statementCount - **E11014**: qualityMetrics.cohesion - **E11015**: qualityMetrics.coupling - **E11019**: qualityMetrics.inheritanceDepth - **E11031**: bannedIdentifiers - **E08070**: commonAIMistakes.aggregateConstructionRules.fieldInitialisationRequired - **E07170**: commonAIMistakes.aggregateConstructionRules.fieldInitialisationRequired - **E08180**: commonAIMistakes.aggregateConstructionRules.fieldInitialisationRequired - **E07200**: commonAIMistakes.aggregateConstructionRules.isSetOperatorRequired - **E07235**: commonAIMistakes.aggregateConstructionRules.isSetOperatorRequired - **E01086**: commonAIMistakes.aggregateConstructionRules.memberOrderingFieldsMethodsOperators - **E50400**: purityRules - **E50401**: purityRules - **E50402**: purityRules - **E50403**: purityRules - **E50404**: purityRules - **E50405**: purityRules - **E07910**: sanitizedTypes - **E07920**: sanitizedTypes - **E07930**: sanitizedTypes - **E07940**: sanitizedTypes - **E07941**: sanitizedTypes - **E07942**: sanitizedTypes - **E07943**: sanitizedTypes - **E06140**: methodResolution - **E50060**: methodResolution - **E06270**: methodResolution - **E01040**: commonAIMistakes.semanticMistakes.overloadingFunctions - **E07950**: commonAIMistakes.semanticMistakes.incrementInExpression - **E08091**: commonAIMistakes.semanticMistakes.unusedParameter - **E07120**: dispatcherPattern - **E07820**: dispatcherPattern - **E05170**: dispatcherPattern - **E05180**: dispatcherPattern - **E06180**: accessModifiers - **E07310**: commonAIMistakes.semanticMistakes.enumSwitchExhaustivenessAndDefault - **E07330**: commonAIMistakes.semanticMistakes.enumSwitchExhaustivenessAndDefault - **E07500**: commonAIMistakes.semanticMistakes.operatorQuestionMustBePure - **E08130**: commonAIMistakes.semanticMistakes.copyConstructorInPureContext - **E08080**: codeQualityEnforcement.tautologicalExpressions - **E08081**: codeQualityEnforcement.tautologicalExpressions - **E08082**: codeQualityEnforcement.tautologicalExpressions - **E08083**: codeQualityEnforcement.tautologicalExpressions - **E08084**: codeQualityEnforcement.tautologicalExpressions - **E08085**: codeQualityEnforcement.tautologicalExpressions - **E11018**: codeQualityEnforcement.unusedCaptureDetection - **E11022**: codeQualityEnforcement.classDesignAntiPatterns - **E11023**: codeQualityEnforcement.classDesignAntiPatterns - **E11024**: codeQualityEnforcement.classDesignAntiPatterns - **E11025**: codeQualityEnforcement.classDesignAntiPatterns - **E11026**: codeQualityEnforcement.referenceOrdering - **E11030**: codeQualityEnforcement.confusinglySimilarNames - **E11032**: codeQualityEnforcement.operatorKeywordShadowing - **E11040**: codeQualityEnforcement.diInjectionLimits - **E11041**: codeQualityEnforcement.diInjectionLimits - **E11042**: codeQualityEnforcement.diInjectionLimits - **E11043**: codeQualityEnforcement.diInjectionLimits - **E11050**: codeQualityEnforcement.discardedReturnValues - **E11051**: codeQualityEnforcement.discardedReturnValues - **E11052**: codeQualityEnforcement.discardedReturnValues - **E11053**: codeQualityEnforcement.dataClumpDetection - **E11054**: codeQualityEnforcement.typeTraversalLimit - **E11061**: codeQualityEnforcement.namedArgumentEnforcement - **E11062**: codeQualityEnforcement.namedArgumentEnforcement - **E11064**: codeQualityEnforcement.magicLiteralDetection - **E11065**: codeQualityEnforcement.magicLiteralDetection - **E11066**: codeQualityEnforcement.constantOrganization - **E11067**: codeQualityEnforcement.constantOrganization - **E11068**: codeQualityEnforcement.stringInterpolationMandate ## AI Quick Reference - **Description**: Most commonly needed pages for AI assistants - **For Errors**: https://ek9.io/errors.html - **For Quality Issues**: https://ek9.io/quality.html - **For Flow Control**: https://ek9.io/flowControl.html - **For Streams**: https://ek9.io/streamsAndPipelines.html - **For Types**: https://ek9.io/builtInTypes.html - **For Testing**: https://ek9.io/testing.html - **For Commands**: https://ek9.io/commandline.html - **For Packaging**: https://ek9.io/packaging.html - **For Dependencies**: https://ek9.io/packaging.html#dependencies - **For Comprehensive Guide**: https://ek9.io/forAI.html ## Oracle Knowledge Base - **Description**: The EK9 Oracle is an in-process knowledge base of compiler-validated Q&A entries with working code snippets. Both frontier and local LLMs MUST consult the Oracle before writing code. - **What It Contains** - **QA Entries**: 565+ Q&A entries across 49 categories (growing toward 2000+) - **Code Snippets**: Every Q&A contains working EK9 code that compiles against the current compiler - **Migration Context**: Language comparison notes for developers coming from Java, Python, C#, etc. - **Error Patterns**: Common mistakes with error codes and corrected examples - **How To Query** - **By Keywords**: ek9 -q — BM25F-ranked search across questions, answers, keywords - **By ID**: ek9 -q — fetch specific Q&A by ID (e.g., ek9 -q 45) - **With Migration**: ek9 -qm — includes language comparison context in results - **By Protocol**: ek9 -qp — search protocol-specific Q&A entries for agent coordination - **List All**: ek9 -q — list all categories and entry counts - **Oracle First Workflow** - **Description**: The Oracle enables a design-first workflow that inverts the industry-standard generate-then-fix cycle - **Steps** - DECOMPOSE: Break the user's request into design components (file I/O, validation, DI, etc.) - CONSULT: Query the Oracle for each component — ask-oracle returns proven, compiled snippets - ASSESS: Check coverage — if all components have snippets, proceed. If gaps exist, report them - COMPOSE: Assemble the solution from Oracle snippets, adapting to the specific requirement - VERIFY: Compile and expect near-clean result (1-2 fix cycles, not 8) - **Why This Works**: Oracle snippets are compiler-validated against the current compiler version. Starting from proven patterns means the first attempt is usually correct. Code-first approaches that guess from general training data take 5-8x more compile cycles. - **Token Savings**: An Oracle-first approach that starts from snippets needs 1-2 compile cycles. A code-first approach needs 8. Over a session with 10 tasks, that is 15 compile cycles vs 80. - **Critical Rule**: ALWAYS consult the Oracle before writing EK9 code. The Oracle contains patterns that are guaranteed to compile. Generating from general training data wastes tokens on doomed implementations. ## AI Agent Architecture - **Description**: When running inside ek9 -ai, LLMs operate in a two-tier system with structured delegation and escalation protocols - **Two Tier Model** - **Frontier**: Cloud LLM (e.g., Claude) — handles architectural reasoning, complex multi-file analysis, design decisions - **Local**: Local LLM (e.g., Qwen2.5-Coder-32B) — handles routine compile-fix loops, Oracle consultation, symbol lookups, test execution - **Ratio**: 70-80% of work is handled locally (zero token cost), 20-30% by frontier (targeted, high-value reasoning) - **Three Message Types** - **Delegation** - **Direction**: Frontier -> Local - **Purpose**: Assign a scoped task (investigation, implementation, or validation) - **Key Fields** - taskId - delegationType - objective - questions/instructions - constraints.responseBudget - terrainBrief - **Protocol Query**: ek9 -qp delegation brief format - **Escalation** - **Direction**: Local -> Frontier - **Purpose**: Request help when stuck (repeated failure, low confidence, out of scope, oracle gap) - **Key Fields** - taskId - originalObjective - escalationReason - currentState - attemptHistory - specificQuestion - **Critical Rule**: Escalation requests must be self-contained — the frontier should be able to help without re-investigating - **Protocol Query**: ek9 -qp escalation request format - **Progress** - **Direction**: Local -> Frontier (fire-and-forget) - **Purpose**: Lightweight status update during long delegations (under 100 tokens) - **Key Fields** - taskId - progress.phase - confidence - estimatedCompletionSeconds - **Protocol Query**: ek9 -qp progress update format - **Intent Vocabulary** - **Description**: Local agents express actions as intents — the in-process infrastructure resolves them against the symbol table, Oracle, and compiler APIs. Never generate shell commands. - **Core Intents** - compile - read-file - write-file - edit-file - search - ask-oracle - type-help - run-tests - **Discovery Intents** - find-definition - find-references - list-methods - list-operators - list-constructors - list-properties - get-type - get-signature - type-hierarchy - find-implementations - list-subtypes - list-module-symbols - list-modules - method-resolution - **Critical Rule**: Discovery intents resolve from the in-process symbol table in ~0.01ms with zero I/O. ALWAYS prefer discovery intents over search for structural queries (finding definitions, listing methods, tracing hierarchies). The symbol table has this information indexed and typed — it cannot produce false positives. - **Protocol Query**: ek9 -qp intent vocabulary ## Bootstrap Sequence - **Description**: What both frontier and local LLMs should do when starting an ek9 -ai session - **Steps** - **Step**: 1 - **Action**: Load this forAI.json reference - **Purpose**: Understand EK9's excluded features, tri-state semantics, closed types, quality metrics, and operator semantics - **Step**: 2 - **Action**: Know the Oracle exists and how to query it - **Purpose**: 565+ Q&A entries with compiler-validated code snippets are available via ask-oracle intent or ek9 -q. Always consult before coding. - **Step**: 3 - **Action**: Know the intent vocabulary - **Purpose**: 22 intents available — 8 core + 14 discovery. Discovery intents use the in-process symbol table (~0.01ms). Never fork to shell for structural queries. - **Step**: 4 - **Action**: Know the delegation protocol - **Purpose**: Frontier delegates via structured briefs, local agents respond with compressed findings. Escalation when stuck. Protocol Q&A available via ek9 -qp. - **Step**: 5 - **Action**: Know the compiler is in-process - **Purpose**: Compilation is a Java method call (~80ms), not fork+exec (~2000ms). The JVM is warm, bootstrap is cached. Re-compile freely — it costs nothing. - **For Frontier LLM**: You coordinate. Delegate investigation and routine implementation to local agents. Retain architectural reasoning and novel design. Set terrain context in delegation briefs. - **For Local LLM**: You execute. Consult the Oracle before coding. Use discovery intents for structural queries. Stay within response budgets. Escalate when stuck — do not guess. - **In Process Advantage**: The EK9 compiler runs in the same JVM. Symbol table queries cost ~0.01ms. Oracle queries cost ~3ms. Compilation costs ~80ms. There is no fork/exec, no JVM cold start, no stderr parsing. Everything is structured JSON from typed APIs. ## AI Workflow Guidance - **Description**: Guidance for AI assistants to produce better EK9 code - **Plan First** - **Description**: Always create a plan before writing EK9 code - **Rule**: Present the plan to the user and ask for feedback before implementing - **Questions To Ask** - Do you want standalone functions, or a trait/class design with operators? - Should I use 'default operator' for auto-generated $, $$, :=:, ==, <=> operators? - Do you need a component/application (DI) architecture, or a simpler program? - Should records use 'default operator $$' for JSON serialization? - **Design Pattern Selection** - **Description**: Choose the right EK9 pattern for the task - **Patterns** - **Name**: Functions only - **When**: Simple utilities, stateless transformations, scripts - **Example**: ```ek9 String formatting, math operations, data transformation ``` - **Name**: Trait + Class - **When**: CRUD operations, stateful services, polymorphic behavior - **Example**: ```ek9 Database access, customer management, repository pattern ``` - **Rule**: If you define a trait, you MUST implement it with a class - never abandon a trait for standalone functions - **Name**: Component + Application - **When**: Dependency injection, service wiring, enterprise architecture - **Example**: ```ek9 Web applications, microservices, multi-component systems ``` - **Name**: Record + default operators - **When**: Data transfer objects, API responses, configuration, value objects - **Example**: ```ek9 Customer record, OrderLine, Config ``` - **Rule**: Always consider 'default operator $$' for JSON and 'default operator $' for string display - **Operator Awareness** - **Description**: EK9 records and classes can have operators - don't write functions for what operators provide - **Critical Rule**: Before writing a function that converts to JSON or String, check if 'default operator $$' or 'default operator $' already does this - **Auto Generated Operators** - **$$**: JSON serialization - produces JSON for all fields automatically - **$**: String representation - produces display string for all fields - **?**: isSet check - returns true if ANY field is set - **:=:**: Deep copy - copies all fields - **==**: Equality comparison - **<=>**: Three-way comparison - **#?**: Hash code - **Example** - **Wrong**: ```ek9 customerToJSON() -> c as Customer <- rtn as String: `{ "name": "${c.name}" }` ``` - **Correct**: ```ek9 //On the Customer record, add: default operator $$ //Then use: $$customer or $$(listOfCustomers) ``` - **Trait Implementation** - **Description**: Traits define interfaces - they must be implemented - **Critical Rule**: If you define a trait, you MUST create a class 'with trait of' that implements it. Never define a trait and then use standalone functions instead. - **Example** - **Wrong**: ```ek9 defines trait CustomerAccess operator += -> customer as Customer defines function addCustomer() //WRONG: standalone function instead of trait impl -> customers as List of Customer -> customer as Customer ``` - **Correct**: ```ek9 defines trait CustomerAccess operator += -> customer as Customer defines class CustomerStore with trait of CustomerAccess customers as List of Customer override operator += -> customer as Customer customers += customer ``` - **Proactive DI Workflow** - **Description**: Before adding or modifying dependency injection, inspect the current DI landscape to avoid compile errors - **When**: Any task involving: adding injection fields, registering components, wiring applications, adding aspects - **Steps** - **Step**: 1 - **Action**: Run 'ek9 -di -E3 main.ek9' to see the current wiring map - **Purpose**: Understand existing registrations, their order, injection sites, and aspect proxies - **Step**: 2 - **Action**: Check if an application block exists - **Purpose**: If no application exists, you must create one — adding injection fields alone will fail - **Step**: 3 - **Action**: Count existing injection fields on the target component - **Purpose**: Components with 5+ injection fields trigger E11040 (excessive coupling) — use a facade instead - **Step**: 4 - **Action**: Identify where to insert new registrations in the ordering - **Purpose**: Dependencies must be registered BEFORE dependents — E08200 fires if order is wrong - **Step**: 5 - **Action**: Add 'default operator ?' to any component with fields - **Purpose**: Components with properties require operator ? for set/unset semantics — E07235 fires if missing - **Common Errors** - **E07235**: Component has fields but no 'default operator ?' — add it after methods - **E07236**: Dynamic class captures variables as fields — add 'default operator ?' at end of body - **E08190**: Circular dependency — extract shared logic into a third component - **E08200**: Registration ordering — reorder so dependencies come first - **E08210**: Missing registration — add registration for the injected abstract type - **E11040**: Too many injection fields — split into smaller components or use a facade - **Error Lookup**: Use 'ek9 -h Exxxx' for full diagnosis, fix actions, and examples for any error code - **Error Diagnosis** - **Description**: When a compiler error occurs, use the built-in tools to understand and fix it - **Steps** - Read the error code (e.g., E50001) from the compiler output - Run 'ek9 -h E50001' for full diagnosis with fix actions and examples - For DI-related errors (E08xxx, E11xxx), also run 'ek9 -di -E3' for the wiring map - Use 'ek9 -q ' to find related Q&A entries - **AI Note**: The 'ek9 -h Exxxx' output is structured as [DIAGNOSIS], [ACTION], [SEE ALSO] — designed for AI parsing ## Inline Examples - **Description**: Self-contained EK9 code examples for AI bootstrapping. These examples are complete and verified to compile. - **Usage**: AI assistants can use these examples directly to understand EK9 syntax and patterns without external file access. - **Reminder**: Use 'ek9 -h ' for help on topics, 'ek9 -h Exxxx' for error diagnosis, 'ek9 -di -E3' for DI wiring map, 'ek9 -q ' for Q&A lookup. - **Hello World** - **Pattern**: basic-program-structure - **Description**: The canonical Hello World - minimal EK9 program structure - **Keywords** - program - module - stdout - println - <- - **Code**: ```ek9 #!ek9 defines module introduction defines program HelloWorld() //Stdout is a built-in type - no import required //The <- operator: declares 'stdout' AND assigns the new Stdout instance //Type inference: compiler knows stdout is type Stdout stdout <- Stdout() stdout.println("Hello, World") //EOF ``` - **Simple Function** - **Pattern**: function-with-parameters - **Description**: Function with parameters and return value - shows -> for input, <- for output - **Keywords** - function - -> - <- - pure - **Code**: ```ek9 #!ek9 defines module example.functions defines function //Single parameter, single return greet() as pure -> name as String <- message as String: "Hello, " + name //Multiple parameters - use indented block after single -> add() as pure -> a as Integer b as Integer <- result as Integer: a + b defines program Demo() stdout <- Stdout() stdout.println(greet("World")) stdout.println(`Sum: ${add(2, 3)}`) //EOF ``` - **Simple Class** - **Pattern**: basic-class-structure - **Description**: Class with properties, constructor, methods, and operator overloading - **Keywords** - class - method - property - operator - $ - **Code**: ```ek9 #!ek9 defines module example.classes defines class Person //Properties are always private firstName <- String() lastName <- String() //Constructor Person() -> firstName as String lastName as String this.firstName :=: firstName this.lastName :=: lastName //Method fullName() as pure <- rtn as String: firstName + " " + lastName //Operator $ for string conversion operator $ as pure <- rtn as String: fullName() //Operator ? for isSet check default operator ? defines program Demo() stdout <- Stdout() person <- Person("Steve", "Limb") stdout.println($person) //EOF ``` - **Class Extension** - **Pattern**: class-extension-hierarchy - **Description**: Abstract and open classes - EK9 types are closed by default - **Keywords** - extends - override - abstract - open - **Code**: ```ek9 #!ek9 defines module example.inheritance defines class //Abstract classes are automatically open Shape as abstract name as String? Shape() -> name as String this.name :=? name area() as pure abstract <- rtn as Float? operator $ as pure <- rtn as String: name ?: "Unknown" //Concrete class extending abstract - closed by default Circle extends Shape radius <- Float() Circle() -> radius as Float super("Circle") this.radius :=? radius override area() as pure <- rtn as Float: 3.14159 * radius * radius //Concrete but marked 'as open' for extension Rectangle extends Shape as open width <- Float() height <- Float() Rectangle() -> width as Float height as Float super("Rectangle") this.width :=? width this.height :=? height override area() as pure <- rtn as Float: width * height //Can extend Rectangle because it's 'as open' Square extends Rectangle Square() -> side as Float super(side, side) defines program Demo() stdout <- Stdout() shapes <- [Circle(5.0), Rectangle(4.0, 3.0), Square(2.0)] for shape in shapes stdout.println(`${shape}: area = ${shape.area()}`) //EOF ``` - **Abstract Function** - **Pattern**: abstract-function-polymorphism - **Description**: Abstract functions as interfaces for polymorphic behavior - **Keywords** - function - abstract - pure - is - **Code**: ```ek9 #!ek9 defines module example.polymorphism defines function //Abstract function - defines interface mathOperation() as pure abstract -> x as Float y as Float <- result as Float? //Concrete implementations add() is mathOperation as pure -> x as Float y as Float <- result as Float: x + y subtract() is mathOperation as pure -> x as Float y as Float <- result as Float: x - y multiply() is mathOperation as pure -> x as Float y as Float <- result as Float: x * y defines program Demo() stdout <- Stdout() //Functions can be stored and called polymorphically for op in [add, subtract, multiply] stdout.println(`Result: ${op(10.0, 5.0)}`) //EOF ``` - **Trait Composition** - **Pattern**: trait-with-delegation - **Description**: Traits for composition, delegation with 'by', and 'allow only' restriction - **Keywords** - trait - with - by - delegate - allow only - **Code**: ```ek9 #!ek9 defines module example.traits defines trait //Simple trait Printable print() abstract <- rtn as String? //Trait inheriting from another Loggable with trait of Printable log() abstract <- rtn as String? //Trait with 'allow only' restriction SecureData allow only EncryptedMessage, SignedMessage data() abstract <- rtn as String? defines class //Class implementing trait Message with trait of Printable content <- String() Message() -> content as String this.content :=? content override print() <- rtn as String: content //Only these can implement SecureData EncryptedMessage with trait of SecureData override data() <- rtn as String: "[encrypted]" SignedMessage with trait of SecureData override data() <- rtn as String: "[signed]" //Delegation with 'by' LoggingMessage with trait of Loggable by printer printer as Printable: Message("default") LoggingMessage() -> printer as Printable this.printer :=? printer override log() <- rtn as String: `[LOG] ${print()}` defines program Demo() stdout <- Stdout() msg <- LoggingMessage(Message("Hello")) stdout.println(msg.log()) //EOF ``` - **Enumeration** - **Pattern**: enumeration-with-operators - **Description**: Enumerations with built-in operators, iteration, and switch integration. CRITICAL: Enumerations automatically get 24 operators from a simple value list declaration - no 'default operator' directives needed. Do NOT write comparison, conversion, lookup, or serialization functions for enumerations - they are all built-in. - **Keywords** - type - enum - for - switch - cat - **AI Warning**: EK9 enumerations are NOT like Java/Python/C# enums. From just a value list (e.g., Colour with Red, Green, Blue), you get 24 operators automatically. Do NOT write: compareEnums() (use < > <= >= <=>), enumToString() (use $), enumToJson() (use $$), findByName() (use Colour(name)), getFirst/getLast() (use #< #>), isValid() (use Colour(name)?), getAllValues() (use cat Colour | collect). ALL of these are built-in. - **Automatic Operators** - **Same Type Comparison** - **Operators** - == - <> - < - > - <= - >= - <=> - **Description**: Compare two enum values by declaration order (ordinal). Red < Blue is true if Red declared before Blue. - **Count**: 7 - **String Param Comparison** - **Operators** - == - <> - < - > - <= - >= - <=> - **Description**: Compare enum to a String directly. colour == "Red" works without conversion. - **Count**: 7 - **String Conversion** - **Operators** - $ - #^ - **Description**: $ returns the value name as String. #^ promotes to String (same result for enums). - **Count**: 2 - **JSON Serialization** - **Operators** - $$ - **Description**: $$ returns JSON representation of the enum value name. - **Count**: 1 - **Hash Code** - **Operators** - #? - **Description**: Ordinal-based hash code, suitable for Dict keys. - **Count**: 1 - **Is Set** - **Operators** - ? - **Description**: Tri-state check. Colour() is unset, Colour.Red is set. - **Count**: 1 - **First Last** - **Operators** - #< - #> - **Description**: #< returns first declared value, #> returns last declared value. - **Count**: 2 - **Constructors** - **Signatures** - Colour() - Colour(Colour) - Colour(String) - **Description**: Default (creates unset), copy, and construct-from-string (unset if no match). - **Count**: 3 - **Total Auto Generated**: 24 - **Code**: ```ek9 #!ek9 defines module example.enumerations defines type CardSuit Hearts Diamonds Clubs Spades CardRank Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten Jack, Queen, King, Ace defines program Demo() stdout <- Stdout() //Access enum values hearts <- CardSuit.Hearts clubs <- CardSuit.Clubs //Comparison operators - ALL built-in, do NOT write comparison functions stdout.println(`hearts < clubs: ${hearts < clubs}`) stdout.println(`hearts == clubs: ${hearts == clubs}`) stdout.println(`hearts <=> clubs: ${hearts <=> clubs}`) //String-param comparison - compare directly to String stdout.println(`hearts == Hearts: ${hearts == "Hearts"}`) //String conversion ($) and JSON ($$) - do NOT write toString/toJson stdout.println(`Suit: ${$hearts}`) stdout.println(`JSON: ${$$hearts}`) //Hash code (#?) - do NOT write hashCode functions stdout.println(`Hash: ${#?hearts}`) //First (#<) and Last (#>) - do NOT write getFirst/getLast stdout.println(`First: ${#< CardSuit}`) stdout.println(`Last: ${#> CardSuit}`) //Construct from string - do NOT write findByName functions validSuit <- CardSuit("Clubs") require validSuit? invalidSuit <- CardSuit("Invalid") require ~invalidSuit? //Is-set check - do NOT write isValid functions require hearts? unknownSuit <- CardSuit() require ~unknownSuit? //Iterate all enum values - do NOT write getAllValues stdout.println("All suits:") for suit in CardSuit stdout.println($suit) //Switch on enum for rank in CardRank message <- switch rank <- rtn as String? case CardRank.Ace rtn: `Wow, an ${rank}!` case > CardRank.Ten rtn: `Face card: ${rank}` default rtn: `Number: ${rank}` stdout.println(message) //Stream pipeline with enum suits <- cat CardSuit | collect as List of CardSuit require suits? //EOF ``` - **Constants** - **Pattern**: constant-declarations - **Description**: Native literal types for constants - all built-in types supported - **Keywords** - constant - literal - Integer - Float - Duration - Date - **Code**: ```ek9 #!ek9 defines module example.constants defines constant //Boolean literals limitRetries <- true updateLog <- false //Character and String delimiter <- ':' author <- "Steve Limb" //Numbers maxRetries <- 10 minTemp <- -2 Pi <- 3.14159 bitMask <- 0b01110001 //Time literals noon <- 12:00 justAfterNoon <- 12:00:01 //Duration literals (ISO 8601) maxDuration <- P3Y2M6DT12H15M6S twoYears <- P2Y twoHours <- PT2H timeout <- 250ms //Date and DateTime millennium <- 2000-01-01 created <- 2018-01-31T01:30:00-05:00 //Money and Colour maxPayment <- 300000#USD defaultColour <- #AB6F2B //Dimension twoMeters <- 2m //RegEx matchSteves <- /[S|s]te(?:ven?|phen)/ //EOF ``` - **If Statement** - **Pattern**: if-statement-comprehensive - **Description**: If statements with guards, declarations, and range checks - **Keywords** - if - else - when - guard - <- - := - ?= - **Code**: ```ek9 #!ek9 defines module example.flowcontrol defines function getTemperature() -> country as String <- temperature <- Integer() if country == "GB" temperature :=? 20 else if country == "US" temperature :=? 75 defines program Demo() stdout <- Stdout() //Simple if score <- 9 if score < 10 stdout.println("Less than 10") //If-else chain result <- String() if score > 9 result: "Too High" else if score < 9 result: "Too Low" else result: "Just Right" stdout.println(result) //'when' is synonym for 'if' when score < 10 stdout.println("Still less than 10") //Guard with declaration - variable scoped to if block //Block only executes if getTemperature returns SET value when reading <- getTemperature("GB") with reading > 10 stdout.println(`UK temp: ${reading}`) //Guard with guarded assignment existingTemp <- Integer() if existingTemp ?= getTemperature("US") with existingTemp > 50 stdout.println(`US is hot: ${existingTemp}`) //Range check x <- 15 if x in 10 ... 20 stdout.println("x is between 10 and 20") if x not in 1 ... 5 stdout.println("x is not between 1 and 5") //EOF ``` - **Switch Statement** - **Pattern**: switch-statement-comprehensive - **Description**: Switch with case, expressions, comparison operators, and guards - NO fallthrough - **Keywords** - switch - given - case - when - default - **Code**: ```ek9 #!ek9 defines module example.switch defines type Status Active, Pending, Closed defines function getCurrentTemp() <- rtn <- 20 defines program Demo() stdout <- Stdout() //'switch' and 'given' are synonyms //'case' and 'when' are synonyms //NO fallthrough - each case is independent (by design) //Basic switch status <- Status.Active switch status case Status.Active stdout.println("Processing...") case Status.Pending stdout.println("Waiting...") default stdout.println("Done") //Switch as expression - returns a value score <- 25 result <- given score <- rtn as String? when < 10 rtn: "Low" when 20, 21, 22, 23, 24 rtn: "Perfect" when > 30 rtn: "High" default rtn: "OK" stdout.println(`Result: ${result}`) //Multiple values in one case (replaces fallthrough) day <- 3 dayType <- switch day <- rtn as String? case 1, 7 rtn: "Weekend" case 2, 3, 4, 5, 6 rtn: "Weekday" default rtn: "Unknown" //Switch with comparison operators multiplier <- 5 category <- switch score <- rtn as String? case < 12 rtn: "Moderate" case > 10*multiplier rtn: "Very High" case getCurrentTemp(), 21, 22 rtn: "Perfect" default rtn: "Suitable" //Switch with guard declaration text <- switch reading <- getCurrentTemp() with reading <- rtn as String? case < 15 rtn: `Cold: ${reading}` case > 25 rtn: `Hot: ${reading}` default rtn: `Nice: ${reading}` stdout.println(text) //EOF ``` - **For Loop** - **Pattern**: for-loop-comprehensive - **Description**: For loops with ranges, collections, iterators - NO break/continue - **Keywords** - for - in - by - range - ... - **Code**: ```ek9 #!ek9 defines module example.forloop defines program Demo() stdout <- Stdout() //Range iteration (inclusive) stdout.println("1 to 5:") for i in 1 ... 5 stdout.println($i) //Range with step stdout.println("1 to 10 by 2:") for i in 1 ... 10 by 2 stdout.println($i) //Descending range stdout.println("10 down to 1:") for i in 10 ... 1 by -1 stdout.println($i) //Collection iteration items <- ["Alpha", "Beta", "Charlie"] for item in items stdout.println(item) //String iteration (characters) for char in "Hello" stdout.println(char) //Iterator directly iter <- items.iterator() for item in iter stdout.println(`Via iterator: ${item}`) //Float range for f in 1.0 ... 5.0 by 0.5 stdout.println($f) //Time range start <- 09:00 finish <- 17:00 for t in start ... finish by PT1H stdout.println($t) //NO break/continue in EK9! //Use streams instead: //cat items | filter by predicate | head //EOF ``` - **While Loop** - **Pattern**: while-loop-comprehensive - **Description**: While and do-while loops with guards and as expressions - **Keywords** - while - do - with - then - **Code**: ```ek9 #!ek9 defines module example.whileloop defines program Demo() stdout <- Stdout() //Basic while items <- ['A', 'B', 'C'] iter <- items.iterator() while iter.hasNext() item <- iter.next() stdout.println(item) //While with guard - reset iterator only if unset while iter ?= items.iterator() then iter.hasNext() item <- iter.next() stdout.println(`Again: ${item}`) //While with declaration - variable scoped to loop while newIter <- items.iterator() then newIter.hasNext() item <- newIter.next() stdout.println(`New: ${item}`) //Do-while - body executes at least once resetIter <- items.iterator() if resetIter.hasNext() do item <- resetIter.next() stdout.println(`Do: ${item}`) while resetIter.hasNext() //While as expression - returns value result <- while complete <- false with not complete <- rtn <- 0 rtn++ complete: rtn == 10 stdout.println(`While result: ${result}`) //Do-while as expression result2 <- do complete <- false <- rtn <- 0 rtn++ complete: rtn == 5 while not complete stdout.println(`Do result: ${result2}`) //EOF ``` - **Try Catch** - **Pattern**: exception-handling-comprehensive - **Description**: Try/catch/finally with guards, custom exceptions, and dispatcher pattern - **Keywords** - try - catch - handle - finally - throw - Exception - **Code**: ```ek9 #!ek9 defines module example.exceptions defines class //Custom exception with additional field ValidationException extends Exception fieldName <- String() ValidationException() -> message as String fieldName as String super(message, 1) this.fieldName :=? fieldName fieldName() as pure <- rtn as String: fieldName override operator $ as pure <- rtn as String: `${reason()} [field: ${fieldName}]` Processor process() -> number as Integer <- rtn as String? if number < 0 throw ValidationException("Negative not allowed", "number") else if number > 100 throw Exception("Too large", 2) else rtn: `Processed: ${number}` //Dispatcher for type-specific exception handling handleError() as dispatcher -> ex as Exception <- rtn as String: $ex handleError() -> ex as ValidationException <- rtn as String: `Validation failed: ${ex.fieldName()}` defines program Demo() stdout <- Stdout() stderr <- Stderr() processor <- Processor() for number in [-1, 50, 150] try result <- processor.process(number) stdout.println(result) catch -> ex as Exception //Dispatcher routes to specific handler stderr.println(processor.handleError(ex)) finally stdout.println(`Finished processing ${number}`) //Try with resource auto-close try -> input <- TextFile("test.txt").input() cat input > stdout handle -> ex as Exception stderr.println($ex) finally stdout.println("File auto-closed") //EOF ``` - **Streams** - **Pattern**: stream-pipelines - **Description**: Stream pipelines replace loops with break/continue - cat, filter, map, head, collect - **Keywords** - cat - | - filter - map - head - tail - collect - > - **Code**: ```ek9 #!ek9 defines module example.streams defines function isEven() as pure -> n as Integer <- rtn as Boolean: n mod 2 == 0 double() as pure -> n as Integer <- rtn as Integer: n * 2 formatItem() as pure -> n as Integer <- rtn as String: `Item: ${n}` joinWithComma() as pure -> a as String b as String <- rtn as String: a? and b? <- a + ", " + b : String() defines program Demo() stdout <- Stdout() numbers <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //Output directly to stdout stdout.println("All numbers:") cat numbers > stdout //Filter - replaces continue pattern stdout.println("Even numbers:") cat numbers | filter by isEven > stdout //Map - transform each item stdout.println("Doubled:") cat numbers | map by double > stdout //Head - replaces break pattern (take first N) stdout.println("First 3:") cat numbers | head 3 > stdout //Tail - last N items stdout.println("Last 3:") cat numbers | tail 3 > stdout //Skip - skip first N stdout.println("Skip first 5:") cat numbers | skip 5 > stdout //Chain multiple operations stdout.println("Even, doubled, first 3:") cat numbers | filter by isEven | map by double | head 3 > stdout //Collect into List evens <- cat numbers | filter by isEven | collect as List of Integer stdout.println(`Collected evens: ${evens}`) //Collect into String with join formatted <- cat numbers | map by formatItem | join with joinWithComma | collect as String stdout.println(`Joined: ${formatted}`) //Range as stream source stdout.println("Range stream:") for i in 1 ... 5 | map by double > stdout //EOF ``` - **Generics** - **Pattern**: generic-types - **Description**: Generic/parameterized types - List of T, Optional of T, custom generics - **Keywords** - of - List - Dict - Optional - generic - constrain - **Code**: ```ek9 #!ek9 defines module example.generics defines function //Generic function identity() of type T -> item as T <- rtn as T: item //Generic with constraint findMax() of type T constrain by Comparable -> a as T b as T <- rtn as T: a > b <- a else b defines class //Generic class Container of type T item as T? Container() -> item as T this.item :=? item get() as pure <- rtn as T: item set() -> newItem as T item :=: newItem operator ? as pure <- rtn as Boolean: item? defines program Demo() stdout <- Stdout() //Built-in generic types strings <- List() of String strings += "Hello" strings += "World" stdout.println(`List: ${strings}`) //Dict (key-value) ages <- Dict() of String, Integer ages += DictEntry("Steve", 30) ages += DictEntry("John", 25) stdout.println(`Dict: ${ages}`) //Optional maybeValue <- Optional(42) of Integer if maybeValue? stdout.println(`Optional has: ${maybeValue.get()}`) empty <- Optional() of String stdout.println(`Empty optional is set: ${empty?}`) //Custom generic container <- Container("test") of String stdout.println(`Container: ${container.get()}`) //Generic functions result <- identity("Hello") of String stdout.println(`Identity: ${result}`) max <- findMax(10, 20) of Integer stdout.println(`Max: ${max}`) //EOF ``` - **Dynamic Functions** - **Pattern**: dynamic-functions-and-closures - **Description**: Dynamic functions with capture - EK9's approach to closures/lambdas. Syntax: '(captures) is/extends AbstractFn as function'. For generic functions: '() extends Function of (In, Out) as pure function'. 'as function' is required on the first alternative or when extending parameterised generic functions. Capture can be unnamed '(threshold)' or named '(lo: minValue)'. Single-line form: '(capture) is fn as pure (rtn: expr)'. CAPTURE SEMANTICS: captures hold the pointer value at capture time — each closure has its own pointer slot, so outer pointer rebinding ('outer := newValue') does NOT affect the closure's captured value. BUT mutating the captured object via '+=', ':=:', ':~:', etc. mutates the SHARED object visible to the caller. For an isolated snapshot, copy via ':=:' or construct a new instance via the type's copy constructor BEFORE capture. Note: 'default operator :=:' is shallow (copies field references) — for deep copy of nested mutable state, the type must implement its own ':=:' that recursively calls ':=:' on each field. See operators.html. Same issue Java has with reference types — EK9 makes the rebind-vs-copy distinction grammatical (':=' vs ':=:'). See QA1296 for capture isolation patterns. - **Keywords** - dynamic - function - capture - is - as function - extends - Predicate - Function - **Code**: ```ek9 #!ek9 defines module example.dynamic defines function //Abstract function to implement predicate() as pure abstract -> number as Integer <- rtn as Boolean? transformer() as pure abstract -> number as Integer <- rtn as Integer? defines program Demo() stdout <- Stdout() threshold <- 10 multiplier <- 3 //Dynamic function capturing 'threshold' — 'as pure function' because predicate is pure greaterThan <- (threshold) is predicate as pure function rtn: number > threshold //Dynamic function capturing 'multiplier' multiply <- (multiplier) is transformer as pure function rtn: number * multiplier //Test the dynamic functions for n in [5, 10, 15] if greaterThan(n) stdout.println(`${n} > ${threshold}: ${multiply(n)}`) //Inline lambda-like syntax (single expression) lessThan <- (threshold) is predicate as pure (rtn: number < threshold) //Multiple captures with named parameters inRange <- (lo: 5, hi: 15) is predicate as pure (rtn: number > lo and number < hi) //Functions as values in list — including inRange predicates <- [ (threshold) is predicate as pure (rtn: number == threshold), greaterThan, lessThan, inRange ] for pred in predicates stdout.println(`10 matches: ${pred(10)}`) //Extending built-in generic functions (Predicate of T, Function of (In, Out)) theValues <- [1, 2, 3, 4, 5] //Indented form with generic Predicate odd <- () extends Predicate of Integer as pure function r: t mod 2 == 0 //Single-line form with generic Function asString <- () extends Function of (Integer, String) as pure function (r : $t) asFloat <- () extends Function of (Integer, Float) as pure function (r : t) captured <- List() of String allAsFloat <- List() of Float cat theValues | tee by asFloat then in allAsFloat | reject odd | map with asString | tee captured > stdout //EOF ``` - **Dynamic Classes** - **Pattern**: dynamic-classes-and-traits - **Description**: Dynamic classes with capture and trait composition. Three forms: (1) Trait-based: '(captures) trait of TraitName as class' or '() with trait of TraitName as class' — both forms valid; 'with' is a stylistic addition. (2) Tuple/named: 'TupleName(captures) as class' — creates a reusable type with methods and operators. (3) Generic extension: '() extends GenericClass of Type as class' — note 'extends' on a dynamic class works ONLY for generic parameterization ('of T'); it cannot extend a non-generic concrete class. For non-generic extension + traits combos, author a named top-level class instead. Dynamic classes generally end with 'default operator ?' (or explicit 'operator ?'); when extending a class that already provides a concrete 'operator ?', the dynamic class inherits it and need not redeclare. Multi-trait delegation: 'trait of T1 by impl1, T2 by impl2' delegates trait methods to captured implementations. CAPTURE SEMANTICS: captures hold the pointer value at capture time — each instance has its own pointer slots, so outer pointer rebinding does NOT affect captured fields. BUT mutating the captured object via '+=', ':=:', ':~:', etc. mutates the SHARED object visible to the caller. For an isolated snapshot, copy via ':=:' or copy constructor BEFORE capture. Note: 'default operator :=:' is shallow (copies field references) — for deep copy of nested mutable state, the type must implement its own ':=:' that recursively calls ':=:' on each field. See operators.html. Same issue as Java reference types — EK9 makes the rebind-vs-copy distinction grammatical (':=' vs ':=:'). See QA1296 for capture isolation patterns. - **Keywords** - dynamic - class - trait - as class - by - capture - tuple - with trait of - **Code**: ```ek9 #!ek9 defines module example.dynamicclasses defines trait Describable describe() abstract <- rtn as String? Printable format() abstract <- rtn as String? defines class //Generic abstract class for extension Processor of type T as abstract process() as abstract -> item as T <- rtn as String? override operator ? as pure <- rtn as Boolean: true defines program Demo() stdout <- Stdout() //--- Form 1: Trait-based dynamic class --- //Simple trait implementation with capture greeting <- "Hello" greeter <- (greeting) with trait of Describable as class override describe() <- rtn as String: greeting default operator ? stdout.println(greeter.describe()) //Named capture (field name differs from source variable) messageText <- "Welcome" printer <- (msg: messageText) with trait of Printable as class override format() <- rtn as String: msg default operator ? stdout.println(printer.format()) //--- Multi-trait delegation with 'by' --- //Compose traits by delegating to captured implementations combo <- (greeter, printer) trait of Describable by greeter, Printable by printer default operator ? //combo.describe() delegates to greeter, combo.format() delegates to printer stdout.println(combo.describe()) stdout.println(combo.format()) //--- Mixed: delegate some traits, implement others inline --- mixed <- (printer) trait of Describable, Printable by printer override describe() <- rtn as String: `Mixed: ${format()}` default operator ? stdout.println(mixed.describe()) //--- Form 2: Tuple/named dynamic class --- //Creates a reusable type 'PersonTuple' with methods and operators personName <- "Steve" born <- 1970-01-01 person <- PersonTuple(name: personName, dob: born) as class personsName() <- rtn as String: name operator $ as pure <- rtn as String: `${name} born ${dob}` default operator ? stdout.println($person) stdout.println(person.personsName()) //Tuple type is reusable — can call constructors and use in functions person2 <- PersonTuple("Jane", 1985-06-15) stdout.println($person2) //--- Form 3: Generic extension dynamic class --- //Extend a parameterised generic class inline stringProcessor <- () extends Processor of String as class override process() -> item as String <- rtn as String: `Processed: ${item}` stdout.println(stringProcessor.process("data")) //EOF ``` - **Ternary Operators** - **Pattern**: ternary-and-coalescing - **Description**: Ternary conditional, null coalescing, elvis, min/max selection operators - **Keywords** - <- - : - ?? - ?: - ? - **Code**: ```ek9 #!ek9 defines module example.ternary defines program Demo() stdout <- Stdout() //Ternary conditional: condition <- trueValue : falseValue x <- 15 result <- x > 10 <- "big" : "small" stdout.println(`x is ${result}`) //isSet check in condition name <- String() display <- name? <- name : "Anonymous" stdout.println(`Display: ${display}`) //Null coalescing ?? - return left if SET, else right value1 <- String() value2 <- "Default" coalesced <- value1 ?? value2 stdout.println(`Coalesced: ${coalesced}`) //Elvis ?: - same as ?? (isSet check) elvis <- value1 ?: "Elvis default" stdout.println(`Elvis: ${elvis}`) //Min selection ? - return larger value maxVal <- a >? b stdout.println(`Max: ${maxVal}`) //Works with unset values - returns the SET one unsetNum <- Integer() setNum <- 42 selected <- unsetNum . Records can have ALL operators (unlike structs in other languages). Use 'default operator' for auto-generation. - **Keywords** - record - :=: - :~: - <~> - <=> - extends - $$ - $ - default - **Code**: ```ek9 #!ek9 defines module example.records defines type //Constrained type PositiveId as Integer constrain as > 0 defines record //Base record IdRecord as abstract id <- PositiveId() IdRecord() -> id as PositiveId require id? this.id :=? id operator $ as pure <- rtn as String: $id operator ? as pure <- rtn as Boolean: id? //Extended record with manual operators Person extends IdRecord firstName as String: String() lastName as String: String() Person() -> id as PositiveId firstName as String lastName as String super(id) this.firstName :=? firstName this.lastName :=? lastName //Copy operator - deep copy all fields operator :=: -> other as Person id :=: other.id firstName :=: other.firstName lastName :=: other.lastName //Merge operator - only copy unset fields operator :~: -> other as Person if not id? id :=: other.id if not firstName? firstName :=: other.firstName if not lastName? lastName :=: other.lastName //Fuzzy match - similarity score operator <~> as pure -> other as Person <- rtn as Integer: (firstName <~> other.firstName) + (lastName <~> other.lastName) //Comparison operator <=> as pure -> other as Person <- rtn as Integer: (lastName <=> other.lastName) + (firstName <=> other.firstName) override operator $ as pure <- rtn as String: `${super.$()} ${firstName} ${lastName}` override operator ? as pure <- rtn as Boolean: super.?() and firstName? and lastName? //Record using 'default operator' for auto-generated implementations //Records can have ALL operators - don't write manual conversion functions! Customer name as String: String() email as String: String() age as Integer: Integer() Customer() -> name as String email as String age as Integer this.name :=? name this.email :=? email this.age :=? age //Auto-generated JSON: {"name":"...","email":"...","age":...} default operator $$ //Auto-generated String display default operator $ //Auto-generated deep copy, equality, isSet, comparison, hashcode default operator :=: default operator == default operator ? default operator <=> default operator #? defines program Demo() stdout <- Stdout() p1 <- Person(PositiveId(1), "Steve", "Limb") p2 <- Person(PositiveId(2), "Stephen", "Limb") //Fuzzy match similarity <- p1 <~> p2 stdout.println(`Similarity: ${similarity}`) //Copy p3 <- Person() p3 :=: p1 stdout.println(`Copied: ${p3}`) //Merge partial records partial1 <- Person(PositiveId(1), "Steve", String()) partial2 <- Person(PositiveId(1), String(), "Limb") partial1 :~: partial2 stdout.println(`Merged: ${partial1}`) //Customer with default operators - NO manual JSON functions needed! c1 <- Customer("Steve", "steve@example.com", 30) //$$record produces JSON automatically json <- $$c1 stdout.println(`JSON: ${json}`) //$record produces String automatically stdout.println(`String: ${c1}`) //Works on Lists too - $$ on List of Customer produces JSON array customers <- [c1, Customer("Jane", "jane@example.com", 25)] stdout.println(`All: ${$$customers}`) //EOF ``` - **Sanitization** - **Pattern**: sanitized-input - **Description**: Input sanitization for security - 'sanitized' modifier on String parameters - **Keywords** - sanitized - String - security - InputSanitizer - **Code**: ```ek9 #!ek9 defines module example.security defines function //Sanitized parameter - automatically validated //Block only executes if input passes security checks processInput() -> sanitized input as String <- rtn as String: String() if input? //Safe to use - guaranteed clean rtn: `Processed: ${input}` //Manual sanitization using InputSanitizer manualCheck() -> userInput as String <- rtn as String: String() sanitizer <- InputSanitizer() if clean <- sanitizer.sanitize(userInput) rtn: `Manual clean: ${clean}` defines program Demo() stdout <- Stdout() stderr <- Stderr() //Safe input safeInput <- "Hello World" result <- processInput(safeInput) stdout.println(result) //Potentially malicious input (SQL injection attempt) badInput <- "'; DROP TABLE users; --" result2 <- processInput(badInput) if result2? stdout.println(result2) else stderr.println("Input rejected as unsafe") //Manual sanitization with threat detection sanitizer <- InputSanitizer() threat <- sanitizer.detectThreat(badInput) if threat? stderr.println(`Threat detected: ${threat}`) //EOF ``` - **Dispatcher Pattern** - **Pattern**: dispatcher-double-dispatch - **Description**: Dispatcher pattern - EK9's replacement for instanceof/casting - **Keywords** - dispatcher - method - polymorphism - override - **Code**: ```ek9 #!ek9 defines module example.dispatcher defines class //Base shape class Shape as abstract name() as pure abstract <- rtn as String? Circle extends Shape radius <- Float() Circle() -> radius as Float this.radius :=? radius override name() as pure <- rtn as String: "Circle" radius() as pure <- rtn as Float: radius Square extends Shape side <- Float() Square() -> side as Float this.side :=? side override name() as pure <- rtn as String: "Square" side() as pure <- rtn as Float: side //Renderer uses dispatcher for type-specific behavior Renderer //Dispatcher method - entry point render() as dispatcher -> shape as Shape <- rtn as String: `Unknown shape: ${shape.name()}` //Specific handler for Circle render() -> shape as Circle <- rtn as String: `Circle with radius ${shape.radius()}` //Specific handler for Square render() -> shape as Square <- rtn as String: `Square with side ${shape.side()}` defines program Demo() stdout <- Stdout() renderer <- Renderer() shapes <- [Circle(5.0), Square(3.0)] //Compiler routes to correct overload automatically //NO instanceof, NO casting needed! for shape in shapes stdout.println(renderer.render(shape)) //EOF ``` - **Rest Service** - **Pattern**: rest-web-service - **Description**: REST service with HTTP method mapping - GET, POST, DELETE, PATCH, PUT via operators - **Keywords** - service - REST - GET - POST - DELETE - PATCH - PUT - PATH - REQUEST - CONTENT - CONTEXT - HTTPResponse - HTTPContext - **Code**: ```ek9 #!ek9 defines module example.restservice defines function //Helper to create a non-cacheable HTTP response base plainResponse() <- rtn as HTTPResponse: () with trait of HTTPResponse override cacheControl() as pure <- rtn as String: "no-store,max-age=0" override contentType() as pure <- rtn as String: "application/json" override contentLanguage() as pure <- rtn as String: "en" default operator ? defines service //Service mapped to /items URI path Items :/items //GET with path parameter - maps to /items/{item-id} byId() as GET for :/{item-id} -> id as String :=: PATH "item-id" <- response as HTTPResponse: (id: id, base: plainResponse()) with trait of HTTPResponse by base override status() as pure <- rtn as Integer: id? <- 200 : 404 override content() <- rtn as String: String() default operator ? //POST - operator += maps to HTTP POST operator += :/ -> request as HTTPRequest :=: REQUEST <- response as HTTPResponse: (request: request, base: plainResponse()) with trait of HTTPResponse by base status as Integer: 201 override content() <- rtn as String: request.content() override status() as pure <- rtn as Integer: status default operator ? //DELETE - operator -= maps to HTTP DELETE operator -= :/{id} -> id as String <- response as HTTPResponse: (base: plainResponse()) with trait of HTTPResponse by base override status() as pure <- rtn as Integer: 204 default operator ? //PATCH - operator :~: maps to HTTP PATCH (merge) operator :~: :/{id} -> id as String content as String :=: CONTENT <- response as HTTPResponse: (base: plainResponse()) with trait of HTTPResponse by base override status() as pure <- rtn as Integer: 204 default operator ? //PUT - operator :^: maps to HTTP PUT (replace) operator :^: :/{id} -> id as String content as String :=: CONTENT <- response as HTTPResponse: (base: plainResponse()) with trait of HTTPResponse by base override status() as pure <- rtn as Integer: 200 default operator ? //GET all items at root URI listAll() :/ <- response as HTTPResponse: (base: plainResponse()) with trait of HTTPResponse by base override content() <- rtn as String: "[]" default operator ? //EOF ``` - **Text Properties** - **Pattern**: text-i18n-localization - **Description**: Text constructs for internationalization - locale-specific templates with string interpolation - **Keywords** - text - locale - i18n - interpolation - backtick - template - **Code**: ```ek9 #!ek9 defines module example.text defines record Person firstName as String: String() lastName as String: String() Person() -> firstName as String lastName as String this.firstName :=? firstName this.lastName :=? lastName operator $ as pure <- rtn as String: `${firstName} ${lastName}` defines text for "en" WelcomePage //Backticks for interpolated template strings greeting() -> person as Person `Welcome ${person.firstName} ${person.lastName}` //Quotes for literal (non-interpolated) text mainMessage() "Welcome to our application." //Escaped $ with \$ for literal dollar sign priceInfo() -> amount as String `Total: \$${amount}` Validator tooShort() -> input as String `The value '${input}' is too short` defines text for "de" WelcomePage greeting() -> person as Person `Willkommen ${person.firstName} ${person.lastName}` mainMessage() "Willkommen in unserer Anwendung." priceInfo() -> amount as String `Gesamt: \$${amount}` Validator tooShort() -> input as String `Der Wert '${input}' ist zu kurz` defines program Demo() stdout <- Stdout() person <- Person("Steve", "Limb") //Runtime locale selection via constructor argument english <- WelcomePage("en") stdout.println(english.greeting(person)) stdout.println(english.mainMessage()) stdout.println(english.priceInfo("42.00")) //Switch to German locale deutsch <- WelcomePage("de") stdout.println(deutsch.greeting(person)) //Validation messages follow same locale pattern validator <- Validator("en") stdout.println(validator.tooShort("Hi")) //EOF ``` - **Component Application** - **Pattern**: component-application-di - **Description**: Components, applications, and dependency injection - register bindings and inject with '!' - **Keywords** - component - application - register - injection - DI - **Code**: ```ek9 #!ek9 defines module example.components defines trait Logger log() abstract -> message as String DataStore save() abstract -> content as String <- rtn as Boolean? load() abstract -> id as String <- rtn as String? defines component //Abstract component - defines injection interface AppLogger as abstract logger() as pure abstract <- rtn as Logger? //Concrete component - provides implementation ConsoleLogger is AppLogger override logger() as pure <- rtn as Logger: () with trait of Logger override log() -> message as String Stdout().println(message) default operator ? //Abstract data store component AppDataStore as abstract store() as pure abstract <- rtn as DataStore? //In-memory implementation MemoryDataStore is AppDataStore override store() as pure <- rtn as DataStore: () with trait of DataStore override save() -> content as String <- rtn as Boolean: content? override load() -> id as String <- rtn as String: `Data for ${id}` default operator ? defines application //Application wires components together //register creates singleton bindings: implementation as abstract type MyApp register ConsoleLogger() as AppLogger register MemoryDataStore() as AppDataStore defines program //Program with DI - 'with application of' enables injection Demo() with application of MyApp //The '!' suffix marks fields for dependency injection appLogger as AppLogger! dataStore as AppDataStore! logger <- appLogger.logger() store <- dataStore.store() logger.log("Starting application") if store.save("test payload") logger.log("Data saved successfully") result <- store.load("item-1") if result? logger.log(`Loaded: ${result}`) //EOF ``` - **Transaction Management** - **Pattern**: transaction-management - **Description**: Transaction trait for ACID-compliant operations with try-with-resources auto-cleanup - **Keywords** - transaction - commit - rollback - Closeable - ACID - try-with-resources - Transaction - **Code**: ```ek9 #!ek9 defines module example.transaction defines class //Implement Transaction trait for ACID operations //Transaction extends Closeable - auto-cleanup in try blocks DatabaseTransaction with trait of Transaction committed <- Boolean() override commit() committed: true override rollback() committed: false override isCommitted() as pure <- rtn as Boolean: Boolean(committed) //close is called automatically at end of try block //Safety net: rollback if not committed override operator close if not isCommitted() rollback() defines program Demo() stdout <- Stdout() //try-with-resources: close called automatically try -> txn <- DatabaseTransaction() stdout.println("Processing...") txn.commit() stdout.println(`Committed: ${txn.isCommitted()}`) catch -> ex as Exception stdout.println(`Failed: ${$ex}`) //EOF ``` - **Networking Types** - **Pattern**: networking-types-and-security - **Description**: Network types: IPAddress, CIDR, Hostname, Networking utility, HTTPContext trait, Cookie record, SecurityGate and CORSPolicy abstract functions - **Keywords** - IPAddress - CIDR - Hostname - Networking - HTTPContext - Cookie - SecurityGate - CORSPolicy - network - IP - HTTP - security - **Types** - **IP Address**: IPv4/IPv6 address. Predicates: isV4, isV6, isLoopback, isPrivate, isMulticast, isLinkLocal, isAnyLocal, version. Literal parsing only (no DNS). Zone IDs rejected. - **CIDR**: Network range (address/prefix). Methods: networkAddress, prefixLength, contains (operator) for IPAddress, includesNetwork for CIDR, isV4, isV6. Host bits silently normalized. - **Hostname**: RFC 1123 hostname. Methods: labels (returns List of String), resolve (returns IPAddress). IDN support, auto-lowercase, trailing dot stripped. - **Networking**: Stateless gateway. Constants: loopbackV4/V6, anyV4/V6, privateA/B/C, loopbackV4Net. Env gateway: ipFromEnv, cidrFromEnv, hostnameFromEnv. Composition: cidrOf, resolve. - **HTTP Context**: HTTP request context trait. Methods: principal, isAuthenticated, isSecure, remoteAddress, requestId, header(name), contentType, withPrincipal(identity), cookie(name). Always set. - **Cookie**: HTTP cookie record with secure defaults. Fields: name, cookieValue, maxAge, path, domain, secure (true), httpOnly (true), sameSite (true=Strict). RFC 6265 encoding via $ operator. - **Security Gate**: Abstract function: (HTTPContext) -> HTTPContext?. Returns enriched context on success, unset on rejection (401). Default rejects all. Use: MyGate is SecurityGate. - **CORS Policy**: Abstract function: (String) -> Boolean?. Returns true = origin allowed, false = rejected, unset = no decision (rejects). Default rejects all. ## Keyword Index - **Description**: Quick lookup of keywords to relevant inline examples - **Program** - helloWorld - simpleFunction - **Module** - helloWorld - constants - **Function** - simpleFunction - abstractFunction - **Class** - simpleClass - classExtension - dynamicFunctions - dynamicClasses - **Record** - records - **Trait** - traitComposition - dynamicClasses - **Type** - enumeration - constants - **Enum** - enumeration - **Extends** - classExtension - records - **Override** - classExtension - traitComposition - **Abstract** - abstractFunction - classExtension - **Pure** - simpleFunction - abstractFunction - **If** - ifStatement - **Switch** - switchStatement - enumeration - **Given** - switchStatement - **For** - forLoop - enumeration - **While** - whileLoop - **Try** - tryCatch - **Catch** - tryCatch - **Finally** - tryCatch - **Cat** - streams - **Filter** - streams - **Map** - streams - **Head** - streams - **Tail** - streams - **Collect** - streams - **Pipe** - streams - **Stream** - streams - **Of** - generics - **List** - generics - streams - **Dict** - generics - **Optional** - generics - **Generic** - generics - **Dynamic** - dynamicFunctions - dynamicClasses - **Capture** - dynamicFunctions - dynamicClasses - **Closure** - dynamicFunctions - dynamicClasses - **Tuple** - dynamicClasses - **By** - dynamicClasses - **With Trait Of** - dynamicClasses - **Ternary** - ternaryOperators - **Coalesce** - ternaryOperators - **??** - ternaryOperators - **?:** - ternaryOperators - **Dispatcher** - dispatcherPattern - tryCatch - **Sanitized** - sanitization - **Constant** - constants - **Operator** - simpleClass - records - enumeration - **Guard** - ifStatement - switchStatement - whileLoop - **Service** - restService - **REST** - restService - **GET** - restService - **POST** - restService - **DELETE** - restService - **PATCH** - restService - **PUT** - restService - **PATH** - restService - **REQUEST** - restService - **CONTENT** - restService - **HTTP Response** - restService - **HTTP Request** - restService - **HTTP Context** - restService - networkingTypes - **CONTEXT** - restService - **IP Address** - networkingTypes - **CIDR** - networkingTypes - **Hostname** - networkingTypes - **Networking** - networkingTypes - **Cookie** - networkingTypes - **Security Gate** - networkingTypes - **CORS Policy** - networkingTypes - **Network** - networkingTypes - **Text** - textProperties - **I18n** - textProperties - **Locale** - textProperties - **Interpolation** - textProperties - **Template** - textProperties - **Component** - componentApplication - **Application** - componentApplication - **Register** - componentApplication - **Injection** - componentApplication - **DI** - componentApplication - **Transaction** - transactionManagement - componentApplication - **Transaction** - transactionManagement - **Commit** - transactionManagement - **Rollback** - transactionManagement - **ACID** - transactionManagement - **$** - simpleClass - records - **$$** - records - **Default** - simpleClass - records - enumeration - **JSON** - records - restService - **Default Operator** - simpleClass - records ## MCP Oracle Tools - **Description**: MCP tools available via ek9 -mcp or ek9 -ide for AI agents to query the compiler. All tools return structured JSON. - **Discovery** - **Name**: ek9_find_construct - **Description**: Find type constructs (classes, traits, records, functions, components) by unqualified name across all compiled modules - **Parameters** - **Name**: string (required) — the unqualified type name to search for - **Returns**: Array of matches with fullyQualifiedName, moduleName, genus, sourceFile, line - **Usage**: Use when you know a type name but not its module or file location - **Location Based** - **Name**: ek9_query_symbol - **Description**: Query symbol information at a specific source location - **Parameters** - **File**: string - **Line**: integer - **Returns**: Symbol name, type, genus, category, squirrelled metadata - **Name**: ek9_query_construct - **Description**: Query complete structure of class/record/trait/component at a location - **Parameters** - **File**: string - **Line**: integer - **Returns**: Fields, methods, operators, constructors, inheritance, metrics - **Name**: ek9_query_type_hierarchy - **Description**: Query type hierarchy including super chain, traits, discovered subtypes, and source locations - **Parameters** - **File**: string - **Line**: integer - **Returns**: Super chain, all traits, sealed status, discovered subtypes with source locations, type parameters - **Name**: ek9_query_scope - **Description**: Query scope chain and active guards at a location - **Parameters** - **File**: string - **Line**: integer - **Returns**: Scope chain, active guards, purity constraints, variable visibility - **Name**: ek9_query_dispatchers - **Description**: Query dispatcher handler methods for a dispatcher class - **Parameters** - **File**: string - **Line**: integer - **Returns**: Dispatcher status, dispatch methods, handler list, sealed type info - **Name**: ek9_query_flow - **Description**: Query function/method complexity and flow analysis metrics - **Parameters** - **File**: string - **Line**: integer - **Returns**: Cyclomatic complexity, statement count, nesting depth, purity, expression complexity - **Name**: ek9_query_call_graph - **Description**: Query call graph showing callers and callees of a function/method - **Parameters** - **File**: string - **Line**: integer - **Returns**: Callers, callees, parameters, return type, purity - **Name**: ek9_query_effective_api - **Description**: Query effective API of a type including inherited methods - **Parameters** - **File**: string - **Line**: integer - **Returns**: All callable methods (own + inherited), operators, total count - **Name**: ek9_query_module - **Description**: Query module-level constructs listing - **Parameters** - **File**: string - **Line**: integer - **Returns**: Module name, all constructs with name/genus/line, references - **Name**: ek9_query_pipeline - **Description**: Query stream pipeline analysis - **Parameters** - **File**: string - **Line**: integer - **Returns**: Pipeline stages, source type, terminal operation, element types - **Name**: ek9_query_references - **Description**: Find all references to a symbol across all compiled modules - **Parameters** - **File**: string - **Line**: integer - **Returns**: Declaration location, type references, extends, trait implementations, field types - **Program Level** - **Name**: ek9_query_diagnostics - **Description**: Query compilation diagnostics (errors, warnings) - **Parameters** - **Returns**: Diagnostic list with file, line, severity, message, error code - **Name**: ek9_query_workspace - **Description**: Query workspace structure and source file listing - **Parameters** - **Returns**: Source files, module names, compilation state - **Name**: ek9_query_compilation_state - **Description**: Query current compilation state and phase - **Parameters** - **Returns**: Current phase, compilation status, error count - **Name**: ek9_query_di_wiring - **Description**: Query DI wiring: applications, registrations, injection sites, programs, health - **Parameters** - **Application**: string (optional) — filter to specific application - **Returns**: Applications with registrations, injection sites, satisfaction status, health verdict - **Name**: ek9_query_module_dependencies - **Description**: Query module dependency graph with cross-module references and cycle detection - **Parameters** - **Include Builtins**: boolean (optional, default false) - **Returns**: Modules, dependency edges with kinds and counts, graph metrics - **Utility** - **Name**: ek9_compile - **Description**: Compile the workspace (required before query tools) - **Parameters** - **Returns**: Compilation result with phase reached and error count - **Name**: ek9_version - **Description**: Get EK9 compiler version - **Parameters** - **Returns**: Version string - **Name**: ek9_help - **Description**: Get help on EK9 topics, types, or error codes - **Parameters** - **Topic**: string - **Returns**: Help text for the topic - **Name**: ek9_explain_error - **Description**: Get detailed explanation for an error code - **Parameters** - **Code**: ```ek9 string ``` - **Returns**: Error diagnosis, fix actions, examples - **Name**: ek9_paradigm - **Description**: Get EK9 language paradigm summary - **Parameters** - **Returns**: Design decisions, excluded features, philosophy - **Name**: ek9_ask - **Description**: Search Q&A knowledge base - **Parameters** - **Query**: string - **Returns**: Matching Q&A entries with code examples - **Name**: ek9_format - **Description**: Format EK9 source file - **Parameters** - **File**: string - **Returns**: Formatted source ## Oracle Editing Tools - **Description**: Oracle editing tools that modify EK9 source code. AI agents call these instead of generating EK9 syntax directly. Each tool returns a Changeset with preview, conflicts, and source edits. The concept-to-tool pipeline: (1) ask Q&A about concept, (2) receive syntax + oracleToolHint, (3) call editing tool to produce correct code. - **Principle**: The AI handles intent (what to create/modify); the Oracle handles correctness (EK9 syntax, indentation, insertion position, operator signatures). The model never generates EK9 tokens from its own weights for structural operations. - **Implemented** - **Name**: ek9_add_field - **Description**: Add a typed field to a class, record, or component with genus-specific rules - **Parameters** - **Construct Name**: string — unqualified type name - **Field Name**: string — new field name - **Type Name**: string — field type (e.g., String, Integer) - **Default Value**: string (optional) — initial value (required for records) - **Raw Declaration**: string (alternative) — full EK9 field declaration (e.g., 'name as String!') - **Genus Rules**: Records: public, must have default. Classes: private, default optional. Components: use '!' suffix for injection. - **Validation**: Grammar-validated via ANTLR parser (FieldDeclarationValidator). Rejects duplicates. - **Name**: ek9_add_method - **Description**: Add a method to a class, trait, component, or service with correct signature and body - **Parameters** - **Construct Name**: string — unqualified type name - **Name**: string — method name - **Parameters**: array of {name, type} — method parameters - **Return Type**: string (optional) — return type - **Pure**: boolean — mark as pure - **Is Abstract**: boolean — abstract (traits/abstract classes only) - **Override**: boolean — override inherited method - **Generates**: Method declaration with parameters, return value declaration, require statements for params, and '//AI: Implement' comment in body for local LLM to fill. - **Validation**: Checks genus supports methods. Rejects duplicate signatures (name + parameter types). - **Name**: ek9_add_operator - **Description**: Add operators to classes/records in three modes: all defaults, single default, or full skeleton - **Parameters** - **Construct Name**: string — unqualified type name - **Mode**: 'all_defaults' | 'default' | 'skeleton' - **Operator Symbol**: string (for default/skeleton) — operator symbol (e.g., '==', '$', '?', '<=>') - **Modes** - **All_defaults**: Inserts 'default operator' — compiler generates all standard operators - **Default**: Inserts 'default operator X' — compiler generates single operator - **Skeleton**: Generates full field-based implementation with correct operator signature - **Skeleton Operators**: ?, $, ==, <>, <=>, #?, :=: — each with field-by-field logic. Auto-detects override from super chain. - **Name**: ek9_add_constructor - **Description**: Add constructor to class/record with 5 modes and purity enforcement - **Parameters** - **Construct Name**: string — unqualified type name - **Mode**: 'private_no_arg' | 'default_all' | 'skeleton_all' | 'from_fields' | 'make_all_pure' - **Field Names**: array of string (for from_fields mode) — subset of fields - **Pure**: boolean (for skeleton_all mode) - **Modes** - **Private_no_arg**: Blocks default construction: 'default private ConstructorName()' - **Default_all**: Compiler-generated from all fields (no body) - **Skeleton_all**: Explicit body with ':=:' copy per field - **From_fields**: Constructor from field subset, auto-determines purity - **Make_all_pure**: Retrofits 'as pure' on all existing constructors - **Purity Rule**: If any existing constructor is pure, all new ones must be pure. - **Name**: ek9_expand_default_operator - **Description**: Expand 'default operator' or 'default operator X' into full skeleton body - **Parameters** - **Construct Name**: string — unqualified type name - **Operator Symbol**: string — specific operator, or 'all' for batch expansion - **Cursor Line**: integer — line containing the default operator declaration - **Generates**: Full field-based operator implementation replacing the default declaration. - **Name**: ek9_collapse_to_default - **Description**: Replace explicit operator implementation with 'default operator X' (inverse of expand) - **Parameters** - **Construct Name**: string — unqualified type name - **Operator Symbol**: string — operator to collapse - **Cursor Line**: integer — line containing the operator declaration - **Defaultable Operators** - <=> - == - <> - < - <= - > - >= - ? - $ - $$ - #? - :=: - **Name**: ek9_organize_references - **Description**: Remove unused type references from the references block (like 'optimize imports') - **Parameters** - **File**: string — source file path - **Generates**: Deletes unused reference lines. Removes entire references block if all unused. - **Name**: toggle_block_comment - **Description**: Toggle EK9 block comments ('') around a line range - **Parameters** - **Start Line**: integer — first line (1-based) - **End Line**: integer — last line (1-based) - **Generates**: Adds markers if uncommented, removes if commented. Rejects nesting. - **Name**: ek9_surround_with - **Description**: Wrap selected lines in a control flow construct with correct indentation and //AI: placeholder comments - **Parameters** - **Source Text**: string — full source text - **File Name**: string — source file name - **Start Line**: integer — first line of selection (1-based) - **End Line**: integer — last line of selection (1-based) - **Type**: 'IF' | 'IF_ELSE' | 'TRY_CATCH' | 'TRY_CATCH_FINALLY' | 'FOR_RANGE' | 'WHILE' - **Generates**: Re-indents selected lines +2 spaces inside the control flow wrapper. Adds '//AI:' comments at each placeholder (condition, else branch, catch handler, finally cleanup, range bounds). - **Note**: Critical in EK9 where indentation defines scope — manual re-indentation is error-prone. - **Designed** - **Name**: ek9_scaffold - **Description**: Generate complete construct outline (program, class, record, trait, component, service, application, function, enum, text, generic, dispatcher) - **Status**: Priority 1 — covers 14 construct types, 30 Q&A entries reference this tool - **Name**: ek9_implement - **Description**: Generate stubs for all unimplemented abstract methods from trait/parent - **Status**: Priority 3 — 5 Q&A entries reference this tool - **Name**: ek9_rename_symbol - **Description**: Rename method/function/field/type across workspace - **Status**: Designed - **Name**: ek9_generate_dispatcher - **Description**: Generate dispatcher with exhaustive handlers for sealed hierarchy - **Status**: Priority 8 - **Name**: ek9_generate_delegation - **Description**: Generate trait delegation wiring via 'by' keyword - **Status**: Priority 9 - **Name**: ek9_convert_to_stream - **Description**: Convert for-loop to stream pipeline (cat | filter by | collect as) - **Status**: Priority 5 - **Name**: ek9_convert_to_guard - **Description**: Convert verbose null-check to guard expression - **Status**: Designed - **Name**: ek9_make_pure - **Description**: Add 'as pure' modifier if body is pure - **Status**: Designed - **Expression Level** - **Name**: ek9_insert_guard - **Description**: Insert guard expression in if/switch/for/while/try - **Status**: Priority 2 — unique to EK9, very high LLM error rate - **Name**: ek9_insert_coalescing - **Description**: Insert coalescing expression (??, ?:, ?, <=?, >=?) - **Status**: Priority 4 — operators unique to EK9 - **Name**: ek9_insert_pipeline - **Description**: Insert stream pipeline (cat | filter by | map with | head | collect as) - **Status**: Designed in Section 7.3 - **Name**: ek9_insert_dynamic_function - **Description**: Insert dynamic function with capture ('() is SuperFunction as function') - **Status**: Priority 7 - **Name**: ek9_insert_dynamic_class - **Description**: Insert dynamic class with capture ('() with trait of X as class') - **Status**: Priority 10