EK9 Compiler Error Index

This page provides detailed explanations for all EK9 compiler errors. Each error has a unique code (E01xxx-E50xxx) that helps you quickly find solutions.

Understanding EK9 Error Codes

EK9 error codes use the format E[PP][NNN] where:

Examples

Error   : E08020: 'result' on line 15: might be used before being initialised
             See: https://ek9.io/errors.html#E08020
  ↑
  Phase 08 (PRE_IR_CHECKS) - This error only occurs in phase 8

Error   : E50001: 'symbol' on line 10: is not resolved
             See: https://ek9.io/errors.html#E50001
  ↑
  Common error - Can occur in phases 2, 3, or 4

Tip: Phase-specific errors (E01xxx-E20xxx) tell you exactly which compilation phase detected the issue. Use -Xcp [PHASE_NAME] to stop compilation at that phase for debugging.

Common Errors (E50xxx)

Some errors can occur in multiple compilation phases because they represent fundamental issues that are checked throughout the compilation process. These errors use the special prefix E50.

Example: E50001: NOT_RESOLVED can occur when:

When you see an E50xxx error, check the full error message for context about which phase detected it and what type of symbol was being resolved.

Phase 01: PARSING

Parsing errors are detected during the initial source code parsing phase. These errors represent fundamental syntax and naming violations that prevent the compiler from understanding the structure of your code.

E01010: Invalid Symbol By Reference

Classification: INVALID_SYMBOL_BY_REFERENCE

Description

In the references section, you attempted to import a symbol using only its name (e.g., SomeType). EK9 requires fully qualified references using the format module.name::SymbolName (e.g., other.module::SomeType). Writing just the symbol name without the module path causes this error because the compiler cannot determine which module contains the symbol.

Example (Error)

#!ek9
defines module fuzztest.references.unqualified

  references
    @Error: REFERENCE_CHECKS: INVALID_SYMBOL_BY_REFERENCE
    UnknownType

  defines function
    testFunc()
      <- result as String: "test"

Solution

#!ek9
defines module example
  references
    other.module::SomeType

  defines function
    testFunc()
      <- result as String: "test"

See Also


E01020: Invalid Module Name

Classification: INVALID_MODULE_NAME

Description

The module names org.ek9.lang and org.ek9.math are reserved exclusively for EK9's built-in language features and standard library types (such as String, Integer, List, etc.). Attempting to define a module with either of these exact names causes this error. Module names follow dotted notation (e.g., com.company.project) and can include most EK9 keywords as segments (e.g., com.private.class.module is valid).

Example (Error)

#!ek9
@Error: PARSING: INVALID_MODULE_NAME
defines module org.ek9.lang  //Reserved namespace

//EOF

Solution

#!ek9
defines module com.mycompany.utilities  //Valid module name

//EOF

Note: Only the specific names org.ek9.lang and org.ek9.math are reserved. Other org.ek9.* module names are allowed. Module segments can include EK9 keywords, making names like company.private.function valid.

See Also


E01030: Duplicate Name

Classification: DUPLICATE_NAME

Description

A name has been used that conflicts with an existing function, creating ambiguity in the code. This error occurs when you attempt to use a function's name for a variable, parameter, property, method, or another construct. EK9 prevents such name collisions to ensure clarity about whether a name refers to a function or another symbol. This maintains unambiguous symbol resolution throughout your code.

Example (Error)

#!ek9
defines module bad.name.collisions1

  defines class
    C1
      //This property collides with function of the same name
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: DUPLICATE_NAME
      broken as String?

      C1()
        -> arg0 as String
        this.broken: arg0

      //This also collides with the function (that follows)
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: DUPLICATE_NAME
      broken()
        <- rtn <- true

  defines function
    broken()
      var <- 3
      assert var?

Solution

#!ek9
defines module example

  defines function
    processData()
      <- rtn as Integer: 9

    validFunction()
      //Use a different parameter name - no collision
      -> dataToProcess as String
      <- rtn as Integer: 1

Common Causes

See Also


E01040: Duplicate Type

Classification: DUPLICATE_TYPE

Description

A type name has been used in a way that conflicts with an existing type. This error occurs when you define the same type (class, record, trait, etc.) multiple times, use a type name for a different construct (like a variable or function), or name a constructor the same as an existing type. Each type must have a unique name within its module scope to prevent ambiguity.

Example (Error)

#!ek9
defines module bad.duplicate.constructs

  defines class
    C1
      someMethod()
        -> arg0 as String
        assert arg0?

    @Error: SYMBOL_DEFINITION: DUPLICATE_TYPE
    C1
      someOtherMethod()
        -> arg0 as String
        assert arg0?

Solution

#!ek9
defines module example

  defines class
    Customer
      name as String?
      id as Integer?

      Customer()
        ->
          customerName as String
          customerId as Integer
        this.name: customerName
        this.id: customerId

See Also


E01050: Possible Duplicate Enumerated Value

Classification: POSSIBLE_DUPLICATE_ENUMERATED_VALUE

Description

Enumeration values must be unique when normalized. EK9 detects duplicates by converting all enumeration values to uppercase and removing underscores before checking for uniqueness. This prevents exact duplicates (like Hearts appearing twice) as well as similar values that could cause confusion (like ACTIVE and active, or VALUE_1 and VALUE1). This ensures enumeration values are clearly distinct and unambiguous.

Example (Error)

#!ek9
defines module bad.enumerations.check

  //Deliberately add in Hearts twice
  defines type
    CardSuit as
      Hearts
      Diamonds
      Clubs
      Spades
      @Error: SYMBOL_DEFINITION: POSSIBLE_DUPLICATE_ENUMERATED_VALUE
      Hearts  //Duplicate of Hearts above

Solution

#!ek9
defines module example

  defines type
    Status
      ACTIVE
      INACTIVE
      PENDING  //All distinct values

Note: EK9 normalizes enumeration values by converting to uppercase and removing underscores. This means Status_OK, STATUS_OK, and STATUSOK are all considered duplicates, as they normalize to the same value.

See Also


Phase 02: SYMBOL_DEFINITION

Symbol definition errors occur during the phase where the compiler builds its symbol table. These errors indicate duplicate symbols, conflicting names, or other issues with how types, methods, and properties are defined.

E02010: Duplicate Property Field

Classification: DUPLICATE_PROPERTY_FIELD

Description

A child class or record has declared a property with the same name as a property in its parent class or record. In EK9, you cannot redeclare or shadow properties from parent types. All properties in an inheritance hierarchy must have unique names. If you need different behavior, use a different property name in the child class.

Example (Error)

#!ek9
defines module bad.duplicateproperties.uses

  defines record
    RBase1 as open
      prop1 <- String()
      prop3 <- Date()

    RExtension1 extends RBase1
      @Error: FULL_RESOLUTION: DUPLICATE_PROPERTY_FIELD
      prop3 <- String()  //Duplicate: already defined in RBase1

Solution

#!ek9
defines module example

  defines record
    RBase1 as open
      prop1 <- String()
      prop3 <- Date()

    RExtension1 extends RBase1
      //Use a different property name - parent already has 'prop3'
      extendedInfo <- String()

See Also


E02020: Cannot Support To JSON Duplicate Property Field

Classification: CANNOT_SUPPORT_TO_JSON_DUPLICATE_PROPERTY_FIELD

Description

A child class redeclares a property from its parent class and uses default operator, which would auto-generate the $$ (to JSON) operator. The compiler cannot automatically generate JSON serialization when property names collide because it creates ambiguity—which prop1 should be serialized? You must either manually override operator $$ to handle the duplicate property explicitly, or use a different property name in the child class.

Example (Error)

#!ek9
defines module bad.defaulted.classoperators

  defines class
    CBase1 as open
      prop1 <- String()
      prop2 <- Date()

      operator $$ as pure
        <- rtn as JSON: JSON()

      default operator

    //Duplicate prop1 with default operator $$ causes JSON ambiguity
    CExtension2 extends CBase1
      @Error: FULL_RESOLUTION: CANNOT_SUPPORT_TO_JSON_DUPLICATE_PROPERTY_FIELD
      prop1 <- Dimension()

      default operator  //Cannot generate $$ due to prop1 collision

Solution

Option 1: Manually override operator $$

#!ek9
defines class
  CBase1 as open
    prop1 <- String()
    operator $$ as pure
      <- rtn as JSON: JSON()
    default operator

  CExtension1 extends CBase1
    prop1 <- Dimension()  //OK because we manually handle $$

    override operator $$ as pure
      <- rtn as JSON: JSON()  //Manually handle duplicate property

    default operator  //OK for other operators

Option 2: Use a different property name

#!ek9
defines class
  CBase1 as open
    prop1 <- String()
    operator $$ as pure
      <- rtn as JSON: JSON()
    default operator

  CExtension1 extends CBase1
    extendedProp1 <- Dimension()  //Different name avoids collision
    default operator  //Can auto-generate $$

See Also


E02030: Method Duplicated

Classification: METHOD_DUPLICATED

Description

Multiple constructors, methods, or operators with identical signatures have been defined in the same type (class, record, trait, service, or component). A method signature consists of its name and parameter types—access modifiers (public, private, protected) do NOT differentiate signatures. This error also occurs when you define an explicit operator implementation and also use default operator for the same operator, or when you use default operator twice for the same operator. The compiler cannot determine which implementation to use, creating ambiguity.

Example (Error)

#!ek9
defines module bad.duplicate.recordmethods

  defines record
    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_DUPLICATED
    R1

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_DUPLICATED
      R1()  //First constructor
        var <- "Steve"
        assert var?

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_DUPLICATED
      R1()  //Duplicate constructor with same signature
        var <- "Steve"
        assert var?

Solution

Option 1: Different method names

#!ek9
defines class
  Calculator
    add()
      ->
        a as Integer
        b as Integer

    addWithBonus()  //Different name
      ->
        a as Integer
        b as Integer
    

Option 2: Different parameter types (overloading)

#!ek9
defines class
  Calculator
    add()
      ->
        a as Integer
        b as Integer

    add()  //Different parameter types - valid overload
      ->
        a as Float
        b as Float
    

Option 3: For operators - remove duplicate or choose explicit vs default

#!ek9
defines record
  R1
    prop1 <- 0

    //Choose ONE approach for each operator:
    operator == as pure  //Explicit implementation
      -> arg0 as R1
      <- rtn as Boolean: prop1 == arg0.prop1

    //OR use default (not both):
    //default operator ==
    

See Also


E02040: Duplicate Variable In Capture

Classification: DUPLICATE_VARIABLE_IN_CAPTURE

Description

When creating a dynamic class or function that captures variables from the enclosing scope, duplicate variable names have been specified in the capture list. This error occurs in three scenarios: (1) capturing the same identifier multiple times like (min, min), (2) using the same target name multiple times like (threshold: min, threshold: max), or (3) capturing different variables with the same internal name like (data: value1, data: value2). All three scenarios create duplicate fields in the generated dynamic class, which would cause ambiguity when accessing the captured values.

Example (Error)

#!ek9
defines module fuzz.dynamic.capture.same.variable.twice

  defines function
    discriminator() as abstract
      -> s as String
      <- rtn as Boolean?

  defines program
    TestCaptureSameVariableTwice()
      min <- 10

      //INVALID - capturing same identifier twice (unnamed)
      @Error: SYMBOL_DEFINITION: DUPLICATE_VARIABLE_IN_CAPTURE
      fn1 <- (min, min) is discriminator as function
        rtn: length s > min

Example 2 (Error - Named Captures)

#!ek9
defines module fuzz.dynamic.capture.duplicate.names

  defines trait
    Validator
      validate()
        -> value as String
        <- result as Boolean?

  defines program
    TestDuplicateCaptureNames()
      min <- 10
      max <- 100

      //INVALID - duplicate name 'threshold' in capture
      @Error: SYMBOL_DEFINITION: DUPLICATE_VARIABLE_IN_CAPTURE
      validator <- (threshold: min, threshold: max) with trait of Validator as class
        override validate()
          -> value as String
          <- result as Boolean: length value > threshold

      assert validator?

Solution

Option 1: Capture each variable only once

#!ek9
defines function
  discriminator() as abstract
    -> s as String
    <- rtn as Boolean?

defines program
  Example()
    min <- 9
    max <- 40

    gt <- (min) is discriminator as function
      rtn: length s > min

    lt <- (max) is discriminator
      rtn: length s < max

Option 2: Use unique target names for named captures

#!ek9
defines trait
  Validator
    validate()
      -> value as String
      <- result as Boolean?

defines program
  Example()
    min <- 10
    max <- 100

    //Use different names: 'minThreshold' and 'maxThreshold'
    validator <- (minThreshold: min, maxThreshold: max) with trait of Validator as class
      override validate()
        -> value as String
        <- result as Boolean: length value > minThreshold and length value < maxThreshold

See Also


E02050: Duplicate Trait Reference

Classification: DUPLICATE_TRAIT_REFERENCE

Description

A trait, class, or component has directly referenced the same trait multiple times in its with trait of declaration. Each trait should only be listed once in the declaration. The compiler detects duplicates regardless of whether you use qualified names (like module::Trait) or unqualified names. Note that it is perfectly valid for the same trait to be included multiple times indirectly through the trait hierarchy (e.g., if trait T1 includes trait T2, and your class uses trait T1, getting T2 indirectly is fine). This error only applies to direct, explicit duplication in the trait list.

Example (Error)

#!ek9
defines module bad.trait.use

  defines trait
    T1
      method1()
        <- rtn <- true

    T2
      method2()
        <- rtn <- true

    @Error: TYPE_HIERARCHY_CHECKS: DUPLICATE_TRAIT_REFERENCE
    DuplicatedReferences1 with trait of T1, T2, T1  //T1 listed twice
      method3()
        <- rtn <- true

Solution

#!ek9
defines trait
  Printable
    print()
      <- result as String?

defines trait
  Saveable
    save()
      <- result as Boolean?

defines class
  Document with trait of Printable, Saveable  //Different traits
    override print()
      <- result as String: "Document"

    override save()
      <- result as Boolean: true

See Also


E02060: Duplicate Enumerated Values Present In Switch

Classification: DUPLICATE_ENUMERATED_VALUES_PRESENT_IN_SWITCH

Description

In a switch statement or expression on an enumeration type, the same enumerated value appears in multiple case clauses. This error occurs whether the duplicate appears as separate individual case values (like case Color.RED twice) or when a value appears in a combined case and then again in a later case (like case Color.RED, Color.GREEN followed by case Color.GREEN). Each enumerated value can appear at most once across all case clauses to prevent unreachable code and ensure unambiguous behavior.

Example (Error)

#!ek9
defines module fuzztest.enumswitch.duplicate.single

  defines type
    Direction
      NORTH,
      SOUTH,
      EAST,
      WEST

  defines program
    testDuplicateEnumSingle()
      dir <- Direction.NORTH
      result as String?

      switch dir
        case Direction.NORTH
          result: "Going North"
        case Direction.SOUTH
          result: "Going South"
        case Direction.EAST
          result: "Going East"
        case Direction.WEST
          result: "Going West"
        @Error: FULL_RESOLUTION: DUPLICATE_ENUMERATED_VALUES_PRESENT_IN_SWITCH
        case Direction.NORTH  //Duplicate: NORTH already handled
          result: "North again"

      assert result?

Example 2 (Error - Combined Case)

#!ek9
defines module fuzztest.enumswitch.duplicate.combined

  defines type
    Color
      RED,
      GREEN,
      BLUE,
      YELLOW

  defines program
    testDuplicateEnumCombined()
      color <- Color.RED
      result as String?

      switch color
        case Color.RED, Color.GREEN  //GREEN appears here
          result: "Warm colors"
        case Color.BLUE, Color.YELLOW
          result: "Cool colors"
        @Error: FULL_RESOLUTION: DUPLICATE_ENUMERATED_VALUES_PRESENT_IN_SWITCH
        case Color.GREEN  //Duplicate: GREEN already in combined case above
          result: "Green again"

      assert result?

Solution

#!ek9
defines type
  Status
    ACTIVE
    INACTIVE
    PENDING

  defines function
    processStatus()
      -> status as Status
      switch status
        case ACTIVE
          result <- "Active"

        case INACTIVE  //Different case value
          result <- "Inactive"

        default
          result <- "Other"

See Also


E02070: Service HTTP Path Duplicated

Classification: SERVICE_HTTP_PATH_DUPLICATED

Description

In a service definition, multiple HTTP endpoints have the same path structure with the same HTTP verb. Path structure is determined by normalizing path variables to {} - meaning the actual variable parameter names, types, and order do NOT matter for comparison. For example, :/{userId}/data.html and :/{customerId}/data.html have the same structure (:/{}/data.html) and would conflict if using the same HTTP verb (like GET). This duplicate check is performed on a per-verb basis, so GET :/{id} and POST :/{id} do not conflict. This prevents routing ambiguity - the system must know unambiguously which endpoint should handle an incoming request.

Example (Error)

#!ek9
defines module fuzztest.service.path.duplicate

  defines service
    DuplicatePathService :/api

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_DUPLICATED
      getResource() as GET :/{id}
        -> id as String
        <- rtn as HTTPResponse: () with trait of HTTPResponse

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_DUPLICATED
      getAnotherResource() as GET :/{id}  //Duplicate: same verb, same path
        -> id as String
        <- rtn as HTTPResponse: () with trait of HTTPResponse

Solution

#!ek9
defines service
  UserService :/api

    getResource() as GET :/{id}
      -> id as String
      <- rtn as HTTPResponse: () with trait of HTTPResponse

    getResourceDetails() as GET :/{id}/details  //Different path - OK
      -> id as String
      <- rtn as HTTPResponse: () with trait of HTTPResponse

Note: Different HTTP verbs (GET, POST, PUT, DELETE) on the same path are allowed and represent RESTful best practices. The error occurs when the same verb is used with the same path structure.

See Also


E02080: Delegate And Method Names Clash

Classification: DELEGATE_AND_METHOD_NAMES_CLASH

Description

A function delegate (either as a property/field or as a local variable) has the same name as a method accessible in that scope. The method can be defined in the same class, in a parent class, or in a trait that the class uses. When you call name(), this creates ambiguity: does it invoke the function delegate or the method? EK9 detects this conflict and requires you to use distinct names. Note that regular (non-delegate) variables can have the same name as methods without error, though this is not recommended for code clarity.

Example (Error)

#!ek9
defines module bad.detailed.resolution

  defines function
    SomeFunction() as abstract
      <- rtn as Integer?

  defines class
    C8A
      method1()
        <- rtn as Integer?

        //This time also make a local delegate and then call it.
        @Error: FULL_RESOLUTION: DELEGATE_AND_METHOD_NAMES_CLASH
        method2 <- () is SomeFunction as function (rtn: 22)

        //But it resolves the dynamic function above and NOT method below - hence the error as it is ambiguous
        rtn: method2()

      method2()
        <- rtn <- false

Solution

#!ek9
defines class
  Processor
    processor as ProcessorFunction by default  //Different name for delegate

    process()  //Method name is now unique
      <- result as String: "Processing"

Best Practice: Use clear, distinct names for delegates and methods to avoid confusion. Consider naming delegates with suffixes like Handler, Function, or Delegate.

See Also


Phase 03: DUPLICATION_CHECK

Duplication check errors occur when the compiler validates that references and symbols don't conflict with each other across module boundaries and scopes.

E03010: Construct Reference Conflict

Classification: CONSTRUCT_REFERENCE_CONFLICT

Description

A locally defined construct (class, function, type, etc.) has the same name as a symbol explicitly referenced in the references section. The reference declares your intention to import and use a symbol from another module (or even your own module), but then defining a local construct with the same name creates a contradiction: which one is the "real" symbol to use? This includes self-references where you reference a symbol from your own module and then define it locally.

Example (Error)

#!ek9
defines module construct.reference.conflict.test

  //Import SharedClass and sharedFunction from external module
  references
    external.module.for.conflicts::SharedClass
    external.module.for.conflicts::sharedFunction

  //Define local class with same name - should trigger CONSTRUCT_REFERENCE_CONFLICT
  defines class
    SharedClass
      default SharedClass()

  //Define local function with same name - should trigger CONSTRUCT_REFERENCE_CONFLICT
  defines function
    sharedFunction()
      <- rtn as String: "local"

Solutions

Option 1: Remove the local definition (use the referenced symbol)

#!ek9
defines module fuzztest.references.self

  references
    external.module::selfFunc  //Use the external symbol

  //Don't define selfFunc locally - just use the referenced one
  defines program
    TestProgram()
      result <- selfFunc()  //Calls external.module::selfFunc
      assert result?

Option 2: Remove the reference (define it locally)

#!ek9
defines module fuzztest.references.self

  //Don't reference it - define it locally instead
  defines function
    selfFunc()  //Local definition - no conflict
      <- result as String: "self"

Option 3: Rename the local construct

#!ek9
defines module my.module
  references
    external.module::SharedClass

  defines class
    LocalClass  //Different name - no conflict
      name as String?

See Also


E03020: References Conflict

Classification: REFERENCES_CONFLICT

Description

The same symbol (module::SymbolName) has been referenced multiple times in the references section. This is redundant and creates an unnecessary duplicate reference. Each symbol should only be referenced once. If you need to import multiple symbols from the same module, list each distinct symbol once.

Example (Error)

#!ek9
defines module duplicate.references.test

  //Reference the same class twice - redundant
  references
    external.module.for.conflicts::SharedClass
    external.module.for.conflicts::SharedClass  //Duplicate reference

  defines function
    testFunction()
      <- rtn as String: "test"

Solution

#!ek9
defines module duplicate.references.test

  //Reference each symbol only once
  references
    external.module.for.conflicts::SharedClass

  defines function
    testFunction()
      <- rtn as String: "test"

See Also


E03030: Reference Does Not Resolve

Classification: REFERENCE_DOES_NOT_RESOLVED

Description

A symbol reference (module::Symbol) in the references section cannot be resolved. This occurs when: (1) the referenced module doesn't exist, (2) the symbol doesn't exist in that module (typo or wrong name), (3) case-sensitive mismatch (EK9 is case-sensitive), or (4) attempting to reference built-in types that are automatically available and cannot be explicitly referenced (like String, Integer, etc. from org.ek9.lang). The compiler cannot find the specified symbol in the specified module.

Example 1 (Error - Nonexistent Module)

#!ek9
defines module fuzztest.references.missing.module

  references
    @Error: REFERENCE_CHECKS: REFERENCE_DOES_NOT_RESOLVED
    com.nonexistent.module::SomeType  //Module doesn't exist

  defines function
    testFunc()
      <- result as String: "test"

Example 2 (Error - Nonexistent Symbol)

#!ek9
defines module fuzztest.references.nonexistent.symbol

  references
    @Error: REFERENCE_CHECKS: REFERENCE_DOES_NOT_RESOLVED
    org.ek9lang.base::NonExistentType  //Symbol doesn't exist in module

  defines function
    testFunc()
      <- result as String: "test"

Common Causes

Solutions

Option 1: Fix typos and case sensitivity

#!ek9
defines module my.module
  references
    external.module::SharedClass  //Correct module and symbol name with proper case

Option 2: For built-in types, use them directly (no reference needed)

#!ek9
defines module my.module
  //Don't reference built-in types - they're automatically available
  defines function
    test()
      value <- String()  //String is automatically available
      assert value?

Option 3: Ensure module exists and is accessible

#!ek9
defines module my.module
  references
    company.utilities::Helper  //Ensure company.utilities module exists and exports Helper

Build Order: Ensure that referenced modules are compiled before modules that depend on them. EK9's build system handles this automatically when modules are in the same project.

See Also


Phase 04: REFERENCE_CHECKS

Reference check errors validate that types are used correctly in specific contexts, ensuring type constraints, conversions, and template/generic requirements are met.

E04010: Type Cannot Be Constrained

Classification: TYPE_CANNOT_BE_CONSTRAINED

Description

When creating a type alias using defines type ... as, certain types cannot be aliased or constrained. Specifically, functions, traits, abstract classes, and certain built-in types (like Boolean and JSON) cannot be used as base types for type definitions. EK9 restricts which types can be aliased to maintain type system integrity and prevent confusion about type identity.

Example (Error)

#!ek9
defines module bad.constrainedtype.examples1

  defines function
    FunctionA()
      <- rtn <- true

  defines type
    //Can't alias/constrain functions
    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_CANNOT_BE_CONSTRAINED
    Other1 as FunctionA

    //Can't alias/constrain Boolean
    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_CANNOT_BE_CONSTRAINED
    Other2 as Boolean

    //Can't alias/constrain JSON
    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_CANNOT_BE_CONSTRAINED
    Other3 as JSON

Solution

#!ek9
defines module example

  defines record
    R1
      default R1()

  defines type
    Index as Integer  //OK - can alias Integer

    Name as String  //OK - can alias String

    ValidOther as R1  //OK - can alias records

See Also


E04020: Type Must Be Convertible To String

Classification: TYPE_MUST_BE_CONVERTABLE_TO_STRING

Description

A type is being used in a string interpolation expression (like `Value is ${obj}`), but the type doesn't provide a way to convert to String. EK9 requires types used in string interpolation to implement either the $ (string) operator for direct string conversion, or the #^ (promote to String) operator for type promotion. Without one of these operators, the compiler cannot determine how to represent the object as a string.

Example (Error)

#!ek9
defines module bad.interpolated.strings

  defines class

    //Has no #^ to String and no $ string operator
    C0
      default C0() as pure
      override operator ? as pure
        <- rtn <- false

  defines function

    missingConversionToString1()
      var <- C0()
      @Error: FULL_RESOLUTION: TYPE_MUST_BE_CONVERTABLE_TO_STRING
      aString <- `Value is ${var}`
      assert aString?

Solution

#!ek9
defines module bad.interpolated.strings

  defines class

    C1
      default C1() as pure
      default operator $

  defines function

    correctExampleOfStringInterpolation1()
      var <- C1()
      aString <- `Value is ${var}`
      assert aString?

See Also


E04030: Type Must Extend Exception

Classification: TYPE_MUST_EXTEND_EXCEPTION

Description

A type is being used in an exception-related context (such as throw or catch) but doesn't extend the built-in Exception type. EK9 enforces type safety in exception handling by requiring all thrown and caught objects to inherit from Exception. This prevents throwing arbitrary types (like String, Integer, or custom classes) which would make error handling unpredictable and error-prone.

Example (Error)

#!ek9
defines module bad.trycatchfinally.example

  defines function

    
    invalidExceptionFunction1()
      <- rtn <- 22
      if rtn == 22
        @Error: FULL_RESOLUTION: TYPE_MUST_EXTEND_EXCEPTION
        throw String("An Exception being thrown")

Solution

#!ek9
defines module bad.trycatchfinally.example

  defines function

    exceptionFunction()
      <- rtn <- 22
      if rtn == 22
        throw Exception("An Exception being thrown")

See Also


E04040: Type Must Be Function

Classification: TYPE_MUST_BE_FUNCTION

Description

A type is being used in a context that requires a function or function delegate, but the type is not a function type. This commonly occurs with higher-order functions or stream pipeline operations like call or async.

Example (Error)

#!ek9
defines module bad.streams5

  defines class

    StringCollector
      joined <- String()

      operator |
        -> arg0 as String
        if arg0?
          if joined?
            joined += " " + arg0
          else
            joined: String(arg0)

      override operator ? as pure
        <- rtn as Boolean: joined?

  defines function

    
    BrokenStreamCatCall2()
      collector <- StringCollector()

      @Error: FULL_RESOLUTION: TYPE_MUST_BE_FUNCTION
      cat [1, 2] | call > collector

      assert collector?

Solution

#!ek9
defines module bad.streams5

  defines function

    abstractFunction() as abstract
      <- rtn as String?

    getSteve() is abstractFunction
      <- rtn <- "Steve"

    getLimb() is abstractFunction
      <- rtn <- "Limb"

    
    SimpleStreamCatCall1()
      collector <- StringCollector()

      cat [getSteve, getLimb] | call > collector

      assert collector?

See Also


E04050: Type Must Be Simple

Classification: TYPE_MUST_BE_SIMPLE

Description

When using type inference with the <- declaration operator, the type being inferred must be "simple" - either a literal value or a simple constructor call. Complex expressions involving method calls, operators, or function invocations cannot be used for type inference. This ensures the compiler can reliably determine types early in compilation before all methods and operators are fully resolved.

Example (Error)

#!ek9
defines module bad.inferred.returns

  defines function

    
    invalidExpressionUse1()
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TYPE_MUST_BE_SIMPLE
      <- aBitComplex <- List("Steve").get(0)

Solution

#!ek9
defines module bad.inferred.returns

  defines function

    validSimpleReturn()
      <- rtn <- Integer()

    validListOfStringReturn()
      <- rtn <- ["Steve", "Stephen", "Steven"]

    validDictOfIntegerStringReturn()
      <- rtn <- {1: "Steve", 2: "Stephen", 3: "Steven"}

See Also


E04060: Is Not An Aggregate Type

Classification: IS_NOT_AN_AGGREGATE_TYPE

Description

A type is being used in a stream pipeline operation (like sort, group, or uniq) where an aggregate type (class, record, component) is required, but the type provided is not an aggregate. Stream operations like sorting and grouping need structured data types with properties and methods (such as comparison operators) to function correctly. Functions, function delegates, and built-in simple types (like Integer alone) don't provide this structure. You must stream aggregate types that can be compared, grouped, or otherwise manipulated by the pipeline operation.

Example (Error)

#!ek9
defines module bad.streams6

  defines class

    StringCollector
      joined <- String()

      operator |
        -> arg0 as String
        if arg0?
          if joined?
            joined += " " + arg0
          else
            joined: String(arg0)

      override operator ? as pure
        <- rtn as Boolean: joined?

  defines function

    ExamplePipeLineFunction()
      <- rtn <- true

    
    InvalidComparatorFunctionStreamCatSort6()
      collector <- StringCollector()

      @Error: FULL_RESOLUTION: IS_NOT_AN_AGGREGATE_TYPE
      cat [ExamplePipeLineFunction] | sort > collector
      assert collector?

Solution

#!ek9
defines module bad.streams6

  defines record

    R2
      prop1 as String: String()
      prop2 as Date: Date()

      R2()
        ->
          p1 as String
          p2 as Date
        this.prop1 :=: p1
        this.prop2 :=: p2

      operator <=> as pure
        -> o as R2
        <- rtn as Integer?
        prop1Result <- prop1 <=> o.prop1
        rtn :=? ~prop1Result? or prop1Result == 0 <- prop2 <=> o.prop2 else prop1Result

      default operator $

  defines function

    ComparatorStreamCatSort2()
      collector <- StringCollector()

      cat [R2("last", 2010-10-01), R2("last", 2010-10-02), R2("first", 2010-10-01)] | sort > collector
      assert collector?

See Also


E04070: Not A Template

Classification: NOT_A_TEMPLATE

Description

Type parameters are being provided to a type or function that is not generic/template. Only types and functions explicitly defined as generic can accept type parameters. This error occurs when attempting to parameterize built-in types like Date, String, or user-defined non-generic types.

Example (Error)

#!ek9
defines module bad.use.non.generic

  defines function

    aNonGenericFunction()
      -> arg1 as String
      <- rtn as String: arg1

    badClassUseOfNonGeneric()
      //Failure 1
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_A_TEMPLATE
      notActuallyGeneric <- Date() of String

      //Failure 2
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_A_TEMPLATE
      alsoNotGeneric as Date of String?

Solution

#!ek9
defines module bad.use.non.generic

  defines function

    validClassUse()
      //Use Date without type parameters
      validDate <- Date()

      //Use generic List with type parameters
      validList <- List() of String
      assert validList?

Or define a generic type:

#!ek9
defines class
  GenericContainer of type T  //Generic type
    value as T?

    default GenericContainer()

    GenericContainer()
      -> val as T
      value: val

  defines function
    demo()
      container <- GenericContainer() of String  //OK - generic type

See Also


E04080: Template Type Requires Parameterization

Classification: TEMPLATE_TYPE_REQUIRES_PARAMETERIZATION

Description

A generic/template type is being used without providing the required type parameters. When a type is defined as generic (e.g., of type T), it must be parameterized with concrete types when used in declarations.

Example (Error)

#!ek9
defines module bad.use.conceptual.parameters

  defines class

    C1 of type T
      prop1 as T?

      default C1()
      C1()
        -> arg T
        prop1: arg

  defines function

    InvalidParameterization()
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TEMPLATE_TYPE_REQUIRES_PARAMETERIZATION
      -> arg as C1
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TEMPLATE_TYPE_REQUIRES_PARAMETERIZATION
      <- rtn as C1: arg
      assert arg?

Solution

#!ek9
defines module bad.use.conceptual.parameters

  defines class

    C1 of type T
      prop1 as T?

      default C1()
      C1()
        -> arg T
        prop1: arg

  defines function

    ValidParameterization()
      -> arg as C1 of String
      <- rtn as C1 of String: arg
      assert arg?

See Also


Phase 05: EXPLICIT_TYPE_SYMBOL_DEFINITION

Type symbol definition errors validate inheritance hierarchies, method overriding, and proper use of type system features like this/super, constructors, and purity.

E05020: Circular Hierarchy Detected

Classification: CIRCULAR_HIERARCHY_DETECTED

Description

A circular type or function hierarchy has been detected. This occurs when type A extends type B, which extends type C, which extends back to type A (forming a cycle). Circular inheritance creates an impossible and infinite inheritance chain that cannot be resolved. EK9 detects both direct circular references (A extends B, B extends A) and indirect ones through multiple levels (A → B → C → A). This applies to classes, records, traits, functions, and components.

Example (Error)

#!ek9
defines module bad.classes.hierarchies

  defines class

    //3-way circular: C1 → C3 → C2 → C1
    @Error: TYPE_HIERARCHY_CHECKS: CIRCULAR_HIERARCHY_DETECTED
    C1 is C3 as open
      field1 <- "Steve"
      default C1()

    C2 is C1 as open
      field2 <- "Stephen"
      default C2()

    C3 is C2 as open
      field3 <- "Stephene"
      default C3()

Example 2 (Error - Direct Circular)

#!ek9
defines module bad.classes.hierarchies

  defines class

    //2-way circular: CA → CB → CA
    @Error: TYPE_HIERARCHY_CHECKS: CIRCULAR_HIERARCHY_DETECTED
    CA is CB as open
      field3 <- "Stephene"
      default CA()

    CB is CA as open
      field4 <- "Steven"
      default CB()

Solution

#!ek9
defines class
  BaseClass
    value as String?

defines class
  ClassA extends BaseClass
    data as Integer?

defines class
  ClassB extends BaseClass  //Linear hierarchy - no cycles
    name as String?

See Also


E05030: Not Open To Extension

Classification: NOT_OPEN_TO_EXTENSION

Description

A type (class, record, function, or trait) is not marked as open (or implicitly open via abstract) but is being extended. By default, EK9 types are closed for extension to encourage composition over inheritance and prevent fragile base class problems. To allow a type to be extended, explicitly mark it as open or as abstract (abstract types are implicitly open). This design encourages intentional inheritance hierarchies rather than accidental coupling.

Example (Error)

#!ek9
defines module bad.inherited.classes

  defines class

    //Explicitly open - can be extended
    Class1 as open
      firstName <- "Steve"
      lastName <- "Limb"

    //Abstract classes are implicitly open - can be extended
    Class2 is Class1 as abstract
      dob <- Date()

    //Can extend Class2 because it's abstract (implicitly open)
    Class3 extends Class2
      postCode <- String()

    //ERROR: Class3 is NOT marked as open - cannot be extended
    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_OPEN_TO_EXTENSION
    Class4 is Class3
      country <- String()

Solution

#!ek9
defines module example

  defines class

    Class1 as open
      firstName <- "Steve"

    Class2 is Class1 as abstract
      dob <- Date()

    //Mark Class3 as open to allow further extension
    Class3 extends Class2 as open
      postCode <- String()

    //Now Class4 can extend Class3
    Class4 is Class3
      country <- String()

Design Principle: EK9 favors composition over inheritance. Only mark types as open when inheritance is genuinely the best design choice. Abstract types are implicitly open because they require concrete implementations.

See Also


E05040: Super For Any Not Required

Classification: SUPER_FOR_ANY_NOT_REQUIRED

Description

Calling super when there is no explicit superclass (only the implicit Any base class) is not required and produces an error. All EK9 types automatically and implicitly extend Any, but Any is special—it has no methods to override and no constructor to call. Calling super.method() or super() when your class has no explicit superclass is meaningless because there's no parent implementation to invoke. Only use super when you have an explicit superclass (via extends or is).

Example (Error)

#!ek9
defines module bad.classes.thisandsuper

  defines class

    InvalidClass1
      method1()
        //This is not valid because there is no explicit super (only implicit Any).
        @Error: FULL_RESOLUTION: SUPER_FOR_ANY_NOT_REQUIRED
        super.method1()

Solution

#!ek9
defines class
  MyClass

    default MyClass()
      //No super() call needed for implicit Any base
      value := "initialized"

See Also


E05050: This And Super Must Be First In Constructor

Classification: THIS_AND_SUPER_MUST_BE_FIRST_IN_CONSTRUCTOR

Description

When calling this() or super() as constructor delegation in a constructor, the call must be the first statement. This ensures proper initialization order and prevents accessing uninitialized state or executing logic before the object is properly constructed. Once you've called super() or this() once, you cannot call them again in the same constructor. This rule applies even when the calls appear in nested blocks within the constructor.

Example (Error)

#!ek9
defines module bad.pure.scenarios2

  defines class

    C0 as open
      C0() as pure
        var1 <- true
        assert var1?

    C1 extends C0
      prop1 <- String()

      C1() as pure
        -> arg0 as C1
        //Some comment, but we can call super() here
        super(arg0)

        //ERROR: Cannot call super() again - must be first statement only
        @Error: FULL_RESOLUTION: THIS_AND_SUPER_MUST_BE_FIRST_IN_CONSTRUCTOR
        super(arg0)

        //ERROR: Cannot call this() after super()
        @Error: FULL_RESOLUTION: THIS_AND_SUPER_MUST_BE_FIRST_IN_CONSTRUCTOR
        this()

Solution

#!ek9
defines class

  C0 as open
    C0() as pure
      var1 <- true
      assert var1?

  C1 extends C0
    prop1 <- String()

    C1() as pure
      -> arg0 as C1
      super(arg0)  //First statement - call super once
      //Additional initialization after super()
      this.prop1 :=? "initialized"

See Also


E05060: This And Super Calls Only In Constructor

Classification: THIS_AND_SUPER_CALLS_ONLY_IN_CONSTRUCTOR

Description

Constructor delegation using this() or super() can only be used within constructors. These special calls invoke another constructor and are not regular methods—they cannot be called from regular methods, operators, or functions. If you need to reuse initialization logic, factor it out into a regular method that both constructors and other methods can call.

Example (Error)

#!ek9
defines module bad.pure.scenarios2

  defines class

    C0 as open
      propA <- Integer()

      C0() as pure
        var1 <- true
        assert var1?

      badMethod2()
        <- rtn <- false
        //ERROR: Cannot call constructor from regular method
        @Error: FULL_RESOLUTION: THIS_AND_SUPER_CALLS_ONLY_IN_CONSTRUCTOR
        aCheck <- this().checkMethod()

      checkMethod()
        <- rtn <- true

Solution

#!ek9
defines class

  C0 as open
    propA <- Integer()

    C0() as pure
      var1 <- true
      commonInitialization()

    C0() as pure
      -> arg0 as C0
      this()  //OK - constructor delegation in constructor
      this :=: arg0

    //Factor out common logic into a regular method
    commonInitialization()
      propA: 0

    badMethod2()
      <- rtn <- false
      commonInitialization()  //Call regular method instead
      rtn: true

See Also


E05070: Inappropriate Use Of This

Classification: INAPPROPRIATE_USE_OF_THIS

Description

The this keyword is being used in an inappropriate context. While this can be used as a value (to reference the current object), as a property accessor (this.property), or with specific operators like :=: (copy), :~: (merge), :^: (replace), and mutation operators (+=, etc.), it cannot be used in certain ways. Specifically, this() as a function call is only valid in constructors for constructor delegation. Attempting to call this() in a regular method, function, or other non-constructor context is inappropriate.

Example (Error)

#!ek9
defines module bad.pure.scenarios2

  defines function

    CheckWhyThisCanBeUsed() as pure
      -> arg0 as Boolean
      //You can use 'this' as a value in functions
      someHolderMaybeToBePassedAbout <- this
      assert someHolderMaybeToBePassedAbout?

      //ERROR: Cannot call this() in a function - only in constructors
      @Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_THIS
      this()

Solution

#!ek9
defines function

  CheckWhyThisCanBeUsed() as pure
    -> arg0 as Boolean
    //OK: Use this as a value (function delegate)
    someHolder <- this
    assert someHolder?

    //OK: Call the delegate with proper arguments
    someHolder(true)

See Also


E05080: Inappropriate Use Of Super

Classification: INAPPROPRIATE_USE_OF_SUPER

Description

The super keyword is being used inappropriately. While super can be used with specific operators (like :=:, :~:, :^:) to mutate the superclass portion of an object, it cannot be:

The super keyword refers to the superclass portion of the current object and is only meaningful for accessing inherited properties/methods or applying mutation operators.

Example (Error)

#!ek9
defines module bad.pure.scenarios2

  defines class

    C0 as open
      propA <- Integer()
      default C0()

    C1 extends C0
      prop1 <- String()

      badMethod1()
        <- rtn <- false

        //ERROR: Cannot declare a variable with super as value
        @Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_SUPER
        theSuper <- super

        //ERROR: Cannot assign super to a variable
        @Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_SUPER
        theSuper := super

        //ERROR: Cannot pass super as a parameter
        @Error: FULL_RESOLUTION: INAPPROPRIATE_USE_OF_SUPER
        this.callMethod(super)

      callMethod()
        -> arg0 as C0
        assert arg0?

Solution

#!ek9
defines module bad.pure.scenarios2

  defines class

    C0 as open
      propA <- Integer()
      default C0()

    C1 extends C0
      prop1 <- String()

      goodMethod1()
        <- rtn <- false

        //VALID: Use 'this' instead (C1 is a C0)
        theThis <- this
        theThis := this
        this.callMethod(this)

        //VALID: Apply mutation operators to super
        super :=: this
        super :~: this

      callMethod()
        -> arg0 as C0
        assert arg0?

See Also


E05090: Use Of This Or Super Inappropriate

Classification: USE_OF_THIS_OR_SUPER_INAPPROPRIATE

Description

This error occurs when this or super is used with operators that are semantically invalid for object self-reference. EK9 allows mutation of this and super using specific operators, but disallows operations that would be confusing or nonsensical:

Valid operators for this/super: :=: (copy), :~: (merge), :^: (replace), +=, -=, *=, /=.

Example (Error)

#!ek9
defines module bad.classassignment.use

  defines class

    C1
      p1 as String: String()

      //ERROR: Cannot use initialization operator with this
      C1()
        -> param as C1
        @Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
        this: param

    C2
      p1 as String: String()

      //ERROR: Guarded assignment makes no sense - this is always set
      C2()
        -> param as C2
        @Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
        this :=? param

    C2a
      p1 as String: String()

      //ERROR: Cannot use direct assignment := to this
      C2a()
        -> param as C2a
        @Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
        this := param

    C2b extends C2a
      p2 as String: String()

      //ERROR: Cannot use direct assignment := to super
      C2b()
        -> param as C2a
        @Error: SYMBOL_DEFINITION: USE_OF_THIS_OR_SUPER_INAPPROPRIATE
        super := param

Solution (Valid Mutation Operators)

#!ek9
defines module bad.classassignment.use

  defines class

    C3
      p1 as String: String()

      //VALID: Copy operator - copies values from param into this
      C3()
        -> param as C3
        this :=: param

      operator :=:
        -> param as C3
        this.p1 :=: param.p1

    C4
      p1 as String: String()
      p2 as String: "Steve"

      //VALID: Merge operator - merges values from param into this
      C4()
        -> param as C4
        this :~: param

      operator :~:
        -> param as C4
        this.p1 :~: param.p1
        this.p2 :~: param.p2

See Also


E05100: Override Inappropriate

Classification: OVERRIDE_INAPPROPRIATE

Description

The override modifier is used in a context where it cannot be applied. This error occurs when override is used with constructs that don't support inheritance or method overriding:

Note: If you're trying to use override in a class/trait context where the method doesn't actually override anything, see E05110: Does Not Override.

Example (Error)

#!ek9
defines module bad.programs.examples

  defines program

    //Valid program without override
    Program1()
      someValue <- 1
      assert someValue?

    //ERROR: Programs cannot extend, so override is inappropriate
    @Error: SYMBOL_DEFINITION: OVERRIDE_INAPPROPRIATE
    override Program2()
      someValue <- 1
      assert someValue?

defines module bad.services.use

  defines service

    Addresses :/addresses/something

      //Valid service method
      byId() as GET for :/{address-id}
        -> addressId as AddressId :=: PATH "address-id"
        <- response as HTTPResponse: HTTPResponse()

      //ERROR: Services cannot extend, so override is inappropriate
      @Error: SYMBOL_DEFINITION: OVERRIDE_INAPPROPRIATE
      override anotherInvalidMethod()
        <- incomingContent as String: "Steve"

Solution

#!ek9
defines module bad.programs.examples

  defines program

    //CORRECT: Remove override modifier
    Program2()
      someValue <- 1
      assert someValue?

defines module bad.services.use

  defines service

    Addresses :/addresses/something

      //CORRECT: Remove override modifier
      anotherValidMethod()
        <- incomingContent as String: "Steve"

See Also


E05110: Does Not Override

Classification: DOES_NOT_OVERRIDE

Description

A method is marked with override but there is no matching method in the superclass or traits to actually override. This typically occurs when:

Key principle: Private methods in a superclass are hidden from subclasses. A method with the same signature in a subclass is a new method, not an override.

Example (Error)

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C1 as open
      //Private method - hidden from subclasses
      private someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //ERROR: Cannot override private method - it's hidden
    C3u extends C1
      @Error: FULL_RESOLUTION: DOES_NOT_OVERRIDE
      override someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

    //ERROR: Cannot override private method even with protected
    C4u extends C1
      @Error: FULL_RESOLUTION: DOES_NOT_OVERRIDE
      override protected someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

Solutions

Option 1: Remove override modifier (new method)

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C1 as open
      private someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //CORRECT: This is a NEW method (not overriding private method)
    C3 extends C1
      someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

Option 2: Make superclass method non-private

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C5 as open
      //Protected or public - can be overridden
      protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //CORRECT: Now we ARE overriding
    OkOverrides1 extends C5
      override protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

Design Principle: EK9 requires that override only be used when actually overriding a visible (non-private) method from a superclass or trait.

See Also


E05120: Method Overrides

Classification: METHOD_OVERRIDES

Description

A method in a subclass has the same signature as a method in the superclass (or trait), which means it is overriding the parent method, but the override keyword is missing. EK9 requires explicit declaration of overrides to prevent accidental method shadowing and make inheritance relationships clear.

Key difference from E05110:

Example (Error)

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C5 as open
      //Protected method that CAN be overridden
      protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //ERROR: This IS overriding but missing 'override' keyword
    NeedsToExpressOverrides1 extends C5 as open
      @Error: FULL_RESOLUTION: METHOD_OVERRIDES
      protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    C8 as open
      //Public method that CAN be overridden
      someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //ERROR: This IS overriding but missing 'override' keyword
    NeedsToExpressOverrides2 extends C8
      @Error: FULL_RESOLUTION: METHOD_OVERRIDES
      someMethod()
        -> arg0 as String
        <- rtn as String: arg0

Solution

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C5 as open
      protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //CORRECT: Explicit 'override' keyword
    OkOverrides1 extends C5
      override protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    C8 as open
      someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //CORRECT: Explicit 'override' keyword
    OkOverrides2 extends C8
      override someMethod()
        -> arg0 as String
        <- rtn as String: arg0

Design Principle: Explicit override prevents accidental method shadowing. If you have the same signature as a superclass method, EK9 assumes you intend to override it and requires you to be explicit about that intention.

See Also


E05130: Method Access Modifiers Differ

Classification: METHOD_ACCESS_MODIFIERS_DIFFER

Description

A method in a subclass has the same signature as a method in the superclass but uses a different access modifier. EK9 enforces the Liskov Substitution Principle: overriding methods cannot change visibility in any way.

EK9 Access Modifiers (least to most visible):

  1. private - Only visible within the class
  2. protected - Visible within the class and subclasses
  3. (default/public) - Visible everywhere

Key rule: You cannot change the access modifier when overriding. EK9 is stricter than Java - even expanding visibility is disallowed.

Example (Error)

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C5 as open
      //Protected method
      protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //ERROR: Cannot restrict protected to private
    C6 extends C5
      @Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
      private someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

    //ERROR: Cannot expand protected to public
    C7 extends C5
      @Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
      someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

    C8 as open
      //Public method (no modifier = public)
      someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //ERROR: Cannot restrict public to private
    C9 extends C8
      @Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
      private someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

    //ERROR: Cannot restrict public to protected
    C10 extends C8 as open
      @Error: FULL_RESOLUTION: METHOD_ACCESS_MODIFIERS_DIFFER
      protected someMethod()
        -> arg0 as String
        <- rtn as String: `[ ${arg0} ]`

Solution

#!ek9
defines module bad.overriding.classmethods1

  defines class

    C5 as open
      protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //CORRECT: Keep same access modifier (protected)
    OkOverrides1 extends C5
      override protected someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    C8 as open
      someMethod()
        -> arg0 as String
        <- rtn as String: arg0

    //CORRECT: Keep same access modifier (public)
    OkOverrides2 extends C8
      override someMethod()
        -> arg0 as String
        <- rtn as String: arg0

Liskov Substitution Principle: Derived classes must be substitutable for their base classes. Changing access modifiers violates this principle by altering the contract of the method.

See Also


E05140: Function Signature Does Not Match Super

Classification: FUNCTION_SIGNATURE_DOES_NOT_MATCH_SUPER

Description

A function that uses is to extend another function has a signature that doesn't match the super function. When extending functions, the signature must be identical:

Note: EK9 functions use is keyword for inheritance, not extends.

Example (Error)

#!ek9
defines module bad.overriding.functions

  defines function

    //Base function expecting String parameter
    Function1() as open
      -> arg0 as String
      <- rtn <- true

    //Abstract function expecting String parameter
    AbstractFunction1() as abstract
      -> arg0 as String
      <- rtn as Boolean?

    //ERROR: Parameter type mismatch (Integer vs String)
    @Error: FULL_RESOLUTION: FUNCTION_SIGNATURE_DOES_NOT_MATCH_SUPER
    InvalidFunction1() is Function1
      -> arg0 as Integer
      <- rtn <- true

    //ERROR: Parameter type mismatch (Float vs String)
    @Error: FULL_RESOLUTION: FUNCTION_SIGNATURE_DOES_NOT_MATCH_SUPER
    InvalidFunction2() is AbstractFunction1
      -> arg0 as Float
      <- rtn <- true

Solution

#!ek9
defines module bad.overriding.functions

  defines function

    Function1() as open
      -> arg0 as String
      <- rtn <- true

    AbstractFunction1() as abstract
      -> arg0 as String
      <- rtn as Boolean?

    //CORRECT: Exact signature match
    ValidFunction1() is Function1
      -> arg0 as String
      <- rtn as Boolean: arg0?

    //CORRECT: Exact signature match (can remain abstract)
    ValidFunction2() is AbstractFunction1 as abstract
      -> arg0 as String
      <- rtn as Boolean?

See Also


E05150: Super Is Pure

Classification: SUPER_IS_PURE

Description

A function/method extends a pure super function/method but is not itself marked as pure. Purity is a guarantee in EK9: once something is declared pure, all implementations must maintain that guarantee.

Why this matters: If a caller receives a reference typed as the super (pure) function/method, they rely on the pure guarantee. Allowing impure implementations would break that contract and violate the Liskov Substitution Principle.

Example (Error)

#!ek9
defines module bad.overriding.functions

  defines function

    //Super function is PURE
    Function2() as pure open
      -> arg0 as String
      <- rtn <- true

    //ERROR: Extending pure function without being pure
    @Error: FULL_RESOLUTION: SUPER_IS_PURE
    InvalidFunction5() is Function2
      -> arg0 as String
      <- rtn <- true

Solution

#!ek9
defines module bad.overriding.functions

  defines function

    Function2() as pure open
      -> arg0 as String
      <- rtn <- true

    //CORRECT: Maintain purity guarantee
    ValidFunction() is Function2 as pure
      -> arg0 as String
      <- rtn <- true

See Also


E05160: Super Is Not Pure

Classification: SUPER_IS_NOT_PURE

Description

A function/method tries to be pure while extending a super function/method that is not pure. You cannot add purity constraints when the parent doesn't have them - this would violate the Liskov Substitution Principle.

Why this matters: The super function's contract doesn't guarantee purity. Code that depends on the super type has no expectation of pure behavior. Attempting to add purity in a subtype would create an incompatible contract.

Example (Error)

#!ek9
defines module bad.overriding.functions

  defines function

    //Super function is NOT pure
    Function1() as open
      -> arg0 as String
      <- rtn <- true

    //ERROR: Cannot add purity when super is not pure
    @Error: FULL_RESOLUTION: SUPER_IS_NOT_PURE
    InvalidFunction6() is Function1 as pure
      -> arg0 as String
      <- rtn <- true

Solution

#!ek9
defines module bad.overriding.functions

  defines function

    Function1() as open
      -> arg0 as String
      <- rtn <- true

    //CORRECT: Match parent's purity (not pure)
    ValidFunction() is Function1
      -> arg0 as String
      <- rtn <- true

See Also


E05170: Dispatcher Pure Mismatch

Classification: DISPATCHER_PURE_MISMATCH

Description

A dispatcher method is marked as pure, but one or more of its dispatch target methods (methods with matching name but more specific parameter types) are not marked as pure. All dispatch targets must match the purity of the dispatcher entry point.

Why this matters: When code calls the dispatcher with the general parameter type, it expects pure behavior. The dispatcher might route to any of the specific implementations. If those implementations aren't pure, the purity guarantee is violated.

Example (Error)

#!ek9
defines module bad.dispatchermethods

  defines class

    BadDispatcher4

      //Dispatcher entry point is PURE
      process() as pure dispatcher
        -> arg as Any
        assert arg?

      //ERROR: Dispatch target is NOT pure
      @Error: FULL_RESOLUTION: DISPATCHER_PURE_MISMATCH
      process()
        -> arg as Integer
        assert arg?

Solution

#!ek9
defines module bad.dispatchermethods

  defines class

    GoodDispatcher

      //Dispatcher entry point is pure
      process() as pure dispatcher
        -> arg as Any
        assert arg?

      //CORRECT: All dispatch targets must also be pure
      process() as pure
        -> arg as Integer
        assert arg?

      process() as pure
        -> arg as String
        assert arg?

See Also


E05180: Dispatcher Private In Super

Classification: DISPATCHER_PRIVATE_IN_SUPER

Description

A class defines a dispatcher method, and the superclass has a private method with a matching signature. This is problematic because:

Why this matters: Dispatchers rely on method visibility. If you define a dispatcher expecting it to route to the superclass method, but that method is private, the dispatcher won't work as intended.

Example (Error)

#!ek9
defines module bad.dispatchermethods

  defines class

    SomeBaseClass as open

      //PRIVATE method - hidden from subclasses
      @Error: FULL_RESOLUTION: DISPATCHER_PRIVATE_IN_SUPER
      private process()
        -> arg as Integer
        assert arg?

    BadDispatcher5 extends SomeBaseClass

      //Dispatcher entry point
      process() as dispatcher
        -> arg as Any
        assert arg?

      //This dispatcher CANNOT call the private super method

Solution

#!ek9
defines module bad.dispatchermethods

  defines class

    SomeBaseClass as open

      //CORRECT: Make method protected or public
      protected process()
        -> arg as Integer
        assert arg?

    GoodDispatcher extends SomeBaseClass

      //CORRECT: Now dispatcher can dispatch to super method
      process() as dispatcher
        -> arg as Any
        assert arg?

See Also


E05190: Mix Of Pure And Not Pure Constructors

Classification: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS

Description

A class has both pure and non-pure constructors. EK9 requires consistency: either all constructors must be pure, or none can be pure. Mixing is not allowed.

Why this matters: Purity affects how objects can be constructed and used in pure contexts. Allowing mixed constructor purity would create ambiguity about whether the class can be safely instantiated in pure contexts.

Example (Error)

#!ek9
defines module bad.pure.scenarios1

  defines class

    C1
      prop1 as String?
      prop2 <- "Steve"

      //ERROR: Not pure
      @Error: FULL_RESOLUTION: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS
      default C1()

      //ERROR: Not pure
      @Error: FULL_RESOLUTION: MIX_OF_PURE_AND_NOT_PURE_CONSTRUCTORS
      C1()
        -> arg0 as String
        prop1 :=? arg0

      //One pure constructor - creates inconsistency
      C1() as pure
        ->
          arg0 as String
          arg1 as String
        prop1 :=? arg0
        prop2 :=? arg1

Solutions

Option 1: Make all constructors pure

#!ek9
defines module bad.pure.scenarios1

  defines class

    C1
      prop1 as String?
      prop2 as String?

      //CORRECT: All constructors pure
      default C1() as pure

      C1() as pure
        -> arg0 as String
        prop1 :=? arg0

      C1() as pure
        ->
          arg0 as String
          arg1 as String
        prop1 :=? arg0
        prop2 :=? arg1

Option 2: Remove pure from all constructors

#!ek9
defines module bad.pure.scenarios1

  defines class

    C1
      prop1 as String?
      prop2 <- "Steve"

      //CORRECT: None pure
      default C1()

      C1()
        -> arg0 as String
        prop1 :=? arg0

      C1()
        ->
          arg0 as String
          arg1 as String
        prop1 := arg0
        prop2 := arg1

See Also


E05200: Incompatible Genus Constructor

Classification: INCOMPATIBLE_GENUS_CONSTRUCTOR

Description

Attempting to manually instantiate a construct whose genus (type category) cannot be constructed directly. Certain genus types are managed by the EK9 runtime and dependency injection system and cannot be manually instantiated:

Why this matters: These constructs have special lifecycles and are managed by the EK9 runtime. Manual construction would bypass the framework's management, violating the designed architecture.

Example (Error)

#!ek9
defines module bad.applicationcalls.examples1

  defines application

    ServiceApplication
      //Application definition

  defines program

    AnotherProgram
      //Program definition

    YetAnotherProgram

      //ERROR: Cannot manually construct APPLICATION genus
      @Error: FULL_RESOLUTION: INCOMPATIBLE_GENUS_CONSTRUCTOR
      app <- ServiceApplication()

      //ERROR: Cannot manually construct PROGRAM genus
      @Error: FULL_RESOLUTION: INCOMPATIBLE_GENUS_CONSTRUCTOR
      otherProg <- AnotherProgram()

Solution

#!ek9
defines module bad.applicationcalls.examples1

  defines application

    ServiceApplication
      //Application definition

  defines program

    //CORRECT: Use 'with application' to wire the application
    ServiceProgram with application of ServiceApplication
      stdout <- Stdout()
      stdout.println("Starting ServiceApplication")

    //Programs are entry points - they cannot be constructed,
    //but they can be executed by the EK9 runtime

See Also


Phase 08: PRE_IR_CHECKS (Code Flow Analysis)

These errors are detected during EK9's comprehensive code flow analysis phase. EK9 analyzes all possible execution paths to ensure variables are properly initialized before use, eliminating entire classes of runtime errors at compile time.

E08020: Used Before Initialized

Classification: USED_BEFORE_INITIALISED

Description

EK9's flow analysis detected that a variable might be used before it has been assigned a value on all possible code paths. This is one of the most common errors and represents EK9's commitment to eliminating null pointer exceptions and uninitialized variable bugs at compile time.

Example (Error)

#!ek9
defines module badclass.method.initialisations

  defines class

    BasicClass
      operator < as pure
        -> arg0 BasicClass
        @Error: PRE_IR_CHECKS: RETURN_NOT_ALWAYS_INITIALISED
        <- rtn as Boolean?

        @Error: PRE_IR_CHECKS: USED_BEFORE_INITIALISED
        assert rtn?

Why this fails: The return value rtn is declared but never assigned a value before being used in the assert statement. The compiler's flow analysis detects that assert rtn? accesses an uninitialized variable, which would cause undefined behavior.

Solutions

Option 1: Initialize before conditional

#!ek9
demo()
  result <- "default"  //Initialize first
  if condition
    result: "value"
  assert result?  //Always initialized

Option 2: Add else clause

#!ek9
demo()
  result as String?
  if condition
    result: "value"
  else
    result: "default"  //Initialize on else path too
  assert result?

Option 3: Use guard expression

#!ek9
demo()
  if result <- getValue()  //Only executes if set
    assert result?

Common mistake: Forgetting that if/else branches create separate code paths. EK9 checks ALL paths to ensure the variable is initialized regardless of which path is taken at runtime.

See Also


E08050: Return Not Always Initialized

Classification: RETURN_NOT_ALWAYS_INITIALISED

Description

The return value of a function or method is not initialized on all possible code paths. EK9 requires that if a function declares a return value, it must be initialized on every possible execution path through the function.

Example (Error)

#!ek9
defines module badclass.method.initialisations

  defines class

    BasicClass
      operator < as pure
        -> arg0 BasicClass
        @Error: PRE_IR_CHECKS: RETURN_NOT_ALWAYS_INITIALISED
        <- rtn as Boolean?

        @Error: PRE_IR_CHECKS: USED_BEFORE_INITIALISED
        assert rtn?

Solution

#!ek9
calculateScore()
  <- result as Integer: 0  //Default value

  if someCondition
    result: 100
  //result always has a value

See Also


E08070: Never Initialized

Classification: NEVER_INITIALISED

Description

A variable or property has been declared but is never assigned a value anywhere in the code. This is different from E08020 (might be used before initialized) - this error means the variable is NEVER initialized at all.

Note: This error applies to class/component properties, not local variables. For properties, EK9 requires either initialization at declaration or in a constructor.

Example (Error)

#!ek9
defines class
  ComponentWithUninitProp
    @Error: PRE_IR_CHECKS: NEVER_INITIALISED
    data as String?  //Declared but never initialized

    useData()
      @Error: PRE_IR_CHECKS: NOT_INITIALISED_BEFORE_USE
      result <- data + " processed"  //Using uninitialized property

Solutions

Option 1: Initialize at declaration

#!ek9
defines class
  ComponentWithInitProp
    data as String: "default value"  //Initialize immediately

    useData()
      result <- data + " processed"  //OK

Option 2: Initialize in constructor

#!ek9
defines class
  ComponentWithConstructor
    data as String?

    default ComponentWithConstructor()
      data: "initialized in constructor"

    useData()
      result <- data + " processed"  //OK

See Also


E08010: Used Before Defined

Classification: USED_BEFORE_DEFINED

Description

A variable is being used in an expression before it has been declared (lexically defined) in the code. This is a forward reference error - the variable name appears in an expression before the declaration statement that creates it. EK9 requires strict lexical ordering: declarations must appear before usage within the same scope. Unlike E08020 (used before initialized), this error means the variable declaration itself comes textually after its first use in the source code, creating an impossible reference.

Example (Error)

#!ek9
defines module bad.blockvariable.uses

  defines class

    CheckBase as open
      usedBeforeDefinition()
        @Error: FULL_RESOLUTION: USED_BEFORE_DEFINED
        someVar <- AVariableThatHasNotYetBeenDefined
        assert someVar?

        //Now define the variable - but after the use
        AVariableThatHasNotYetBeenDefined <- "Now Defined"

Solution

#!ek9
demo()
  myValue <- 5  //Declare first
  result <- myValue + 10  //Then use

See Also


E08030: Unsafe Method Access

Classification: UNSAFE_METHOD_ACCESS

Description

An optional variable (declared with ?) is being used to call methods without first checking if it's set. EK9 requires explicit null-safety checks before accessing methods on optional types.

Example (Error)

#!ek9
defines module error.on.optional.access

  defines function

    testInvalidUsingTernary1()
      o <- Optional("Steve")

      @Error: PRE_IR_CHECKS: UNSAFE_METHOD_ACCESS
      value <- o? <- "Something" else o.get()
      assert value?

Solutions

Option 1: Use guard expression

#!ek9
processName()
  name as String?

  if name?  //Check if set
    length <- name.length()  //Safe access

Option 2: Use null-safe method access

#!ek9
processName()
  name as String?

  //Safe method access - returns Optional of Integer
  length <- name?.length()

See Also


E08040: No Reassignment Within Safe Access

Classification: NO_REASSIGNMENT_WITHIN_SAFE_ACCESS

Description

Within a safe access block (after checking variable?), you cannot reassign the variable. This would invalidate the safety guarantee that the compiler provides.

Example (Error)

#!ek9
defines module error.on.result.reassignment

  defines function

    getExplicitResult()
      <- rtn <- Result("Steve", Integer())

      o <- getExplicitResult()

      if o?
        //We should not be allowed to reassign in here, this scope is marked safe access
        //This would break that and make logic very complex if we were to deal with work around.
        @Error: PRE_IR_CHECKS: NO_REASSIGNMENT_WITHIN_SAFE_ACCESS
        o: Result("Stephen2", 2)
        val <- o.ok()
        assert val?

Solution

#!ek9
demo()
  data as String?

  if data?
    result <- data.length()  //Use the value, don't reassign

  //Reassignment OK outside the safe access block
  data := "new value"

See Also


E08060: Not Initialized Before Use

Classification: NOT_INITIALISED_BEFORE_USE

Description

A variable is being used before it has been initialized on the current code path. This is similar to E08020, but specifically applies to property access within classes.

Example (Error)

#!ek9
defines class
  DataProcessor
    value as Integer?

    process()
      @Error: PRE_IR_CHECKS: NOT_INITIALISED_BEFORE_USE
      result <- value * 2  //value never initialized

Solution

#!ek9
defines class
  DataProcessor
    value as Integer: 0  //Initialize at declaration

    process()
      result <- value * 2  //OK

See Also


E08080: Self Assignment

Classification: SELF_ASSIGNMENT

Description

A variable is being assigned to itself (x := x). This is almost always a mistake and has no effect - the variable already contains its own value. EK9 flags this as an error to help catch logic errors, typos, and copy-paste mistakes. This includes both regular assignment (:=) and copy assignment (:=:), as copying a value over itself is equally pointless.

Example (Error)

#!ek9
defines module bad.blockvariable.uses

  defines class

    CheckBase as open
      selfAssignmentInBlockVariable()
        someBlockVariable <- 1

        @Error: FULL_RESOLUTION: SELF_ASSIGNMENT
        someBlockVariable: someBlockVariable

        //Really a copy over self, but still a self assignment
        @Error: FULL_RESOLUTION: SELF_ASSIGNMENT
        someBlockVariable :=: someBlockVariable

Common Cause

This error often occurs when copy-pasting code or during refactoring. Check if you meant to assign a different variable or perform an operation.

Solution

#!ek9
demo()
  value <- 10
  newValue <- 20

  value := newValue  //Assign different variable

E08090: Not Referenced

Classification: NOT_REFERENCED

Description

A variable has been declared and possibly initialized, but is never referenced (used) anywhere in the code. This indicates dead code, wasted computation, or a forgotten implementation. EK9 requires all declared variables to be used, promoting cleaner code and helping identify logic errors where a variable was intended to be used but was accidentally omitted. This check applies to both initialized variables and variable-only declarations.

Example (Error)

#!ek9
defines module bad.blockvariable.uses

  defines class

    CheckBase as open
      unReferencedBlockVariable()
        @Error: FULL_RESOLUTION: NOT_REFERENCED
        someUnReferencedVariable1 <- 1

        @Error: FULL_RESOLUTION: NOT_REFERENCED
        someUnReferencedVariable2 as String?

        aReferencedVariable <- 2
        assert aReferencedVariable?

Solutions

If truly unused: Remove the variable declaration

#!ek9
demo()
  result <- calculateValue()  //Removed unused variable

If needed: Use the variable in your logic

#!ek9
demo()
  multiplier <- 42
  result <- calculateValue() * multiplier  //Now used

E08100: No Pure Reassignment

Classification: NO_PURE_REASSIGNMENT

Description

Within a function/method/constructor marked as pure, you cannot reassign variables using := (reassignment operator). Pure functions must be side-effect free, and reassignment is considered a mutation side effect. This maintains referential transparency - pure functions always produce the same output for the same input. Use :=? (conditional/guarded assignment) for initial assignment only when the variable is unset, or create new variables instead of reassigning existing ones.

Example (Error)

#!ek9
defines module bad.pure.scenarios1

  defines class

    C1 as pure
      prop1 as String?
      prop2 as Integer?

      C1() as pure
        -> arg0 as String
        -> arg1 as Integer

        //So if a constructor is marked as pure use the :=? to assign
        //But this means you cannot have default values really.
        //Well you can but use multiple constructors
        @Error: FULL_RESOLUTION: NO_PURE_REASSIGNMENT
        prop2 := arg1

Solution

#!ek9
calculate() as pure
  <- result as Integer: 0

  result <- 10  //Use initial assignment instead
  //No reassignment needed

See Also


E08110: No Incoming Argument Reassignment

Classification: NO_INCOMING_ARGUMENT_REASSIGNMENT

Description

Function parameters/arguments cannot be reassigned using := or :=?. This prevents confusion about whether a parameter represents the original input value or a modified value during function execution. Parameters are immutable once passed to a function. If you need to work with a potentially different value, create a new local variable instead of attempting to reassign the incoming argument. This promotes clarity and prevents subtle bugs where modified parameters could be mistaken for original inputs.

Example (Error)

#!ek9
defines module bad.pure.scenarios1

  defines function

    someMethod()
      -> arg0 as String?

      //We do NOT allow a null check reassignment - even in pure
      //There is no need - just create a new variable as shown below
      @Error: FULL_RESOLUTION: NO_INCOMING_ARGUMENT_REASSIGNMENT
      arg0 :=? "Stephen"

      //Use this approach instead, rather than reusing incoming arguments.
      newVar <- arg0? <- arg0: "Stephen"
      assert newVar?

Solution

#!ek9
process()
  -> value as Integer
  modified <- value + 10  //Create new variable instead
  result <- modified * 2

E08120: No Mutation In Pure Context

Classification: NO_MUTATION_IN_PURE_CONTEXT

Description

Within a pure function or method, you cannot call mutating operators or methods on objects. Mutating operators like +=, -=, ++, etc. modify object state in place, which violates the purity constraint. Pure functions must have no side effects, including modifying object state - they can only compute and return new values. This ensures referential transparency and makes pure functions safe for optimization, parallelization, and memoization.

Example (Error)

#!ek9
defines module bad.pure.scenarios2

  defines class

    C2 as pure
      prop1 <- R1()

      C2() as pure
        r1 <- R1()

        @Error: FULL_RESOLUTION: NO_PURE_REASSIGNMENT
        r1.value: "James"

        //While this is a field/property - it is not a property on this type
        @Error: FULL_RESOLUTION: NO_MUTATION_IN_PURE_CONTEXT
        r1.value += "Brown"

Solution

#!ek9
processData() as pure
  -> list as List of String
  //Create new list instead of mutating
  <- result as List of String: List(list)
  result += "item"  //OK - mutating local copy

See Also


E08130: Non-Pure Call In Pure Scope

Classification: NONE_PURE_CALL_IN_PURE_SCOPE

Description

A pure function is calling another function/method that is NOT marked as pure. Pure functions can only call other pure functions to maintain the guarantee of no side effects.

Example (Error)

#!ek9
impureFunction()
  <- result as String: "side effect"
  stdout.println("This has side effects!")

calculate() as pure
  @Error: PRE_IR_CHECKS: NONE_PURE_CALL_IN_PURE_SCOPE
  value <- impureFunction()  //Can't call non-pure function

Solution

#!ek9
pureHelper() as pure
  <- result as Integer: 42

calculate() as pure
  value <- pureHelper()  //OK - calling pure function

See Also


E08140: Component Injection In Pure

Classification: COMPONENT_INJECTION_IN_PURE

Description

Dependency injection (using ! syntax) is not allowed in pure functions, methods, or constructors. Dependency injection involves accessing external state managed by the DI container at runtime, which introduces side effects and breaks referential transparency. Pure functions must be deterministic and self-contained, depending only on their input parameters to produce output.

Example (Error)

#!ek9
defines module bad.injection.examples

  defines component

    Comp1
      default Comp1()

  defines function

    TryToInjectInPure() as pure
      @Error: FULL_RESOLUTION: COMPONENT_INJECTION_IN_PURE
      injected as Comp1!
      assert injected?

Solution

Remove the pure marker or pass dependencies as constructor parameters instead of using injection.

#!ek9
defines class
  Processor  //Not marked pure
    service as IService: Injected  //OK

See Also


E08150: Component Injection Of Non-Abstract

Classification: COMPONENT_INJECTION_OF_NON_ABSTRACT

Description

Dependency injection requires an abstract base type (interface/trait/abstract component). You cannot inject concrete (non-abstract) component implementations directly using the ! syntax. This violates the dependency inversion principle - injection should depend on abstractions, not concretions. The DI container needs an abstract type to determine which concrete implementation to provide based on configuration. Use abstract components for injection, allowing different concrete implementations to be wired at deployment time.

Example (Error)

#!ek9
defines module bad.injection.examples

  defines component

    Comp2
      default Comp2()

  defines function

    TryToInjectNonAbstractComponent()
      //This won't work because it's not abstract (but it is component)
      @Error: FULL_RESOLUTION: COMPONENT_INJECTION_OF_NON_ABSTRACT
      injected as Comp2!

      assert injected?

defines class
  Consumer
    @Error: PRE_IR_CHECKS: COMPONENT_INJECTION_OF_NON_ABSTRACT
    service as ConcreteService: Injected  //Can't inject concrete type

Solution

#!ek9
defines trait
  IService
    process()
      <- result as String?

defines class
  ConcreteService with trait of IService
    override process()
      <- result as String: "done"

defines class
  Consumer
    service as IService: Injected  //OK - injecting abstract type

See Also


E08160: Component Injection Not Possible

Classification: COMPONENT_INJECTION_NOT_POSSIBLE

Description

Dependency injection (using ! syntax) is being attempted in a context where it's not supported. Injection is only allowed for properties/fields in classes and components, not in function-local variables, method parameters, or primitive types. The dependency injection system needs stable, class-scoped storage to manage component lifecycles. Use constructor parameters or method parameters to pass dependencies into functions, or restructure code to use class-based DI if needed.

Example (Error)

#!ek9
defines module bad.injection.examples

  defines function

    TryToInjectNonComponent()
      //This won't work because it's not a component
      @Error: FULL_RESOLUTION: COMPONENT_INJECTION_NOT_POSSIBLE
      injected as String!

      assert injected?

Solution

Pass dependencies as parameters or use class-based dependency injection.

#!ek9
defines class
  Processor
    service as IService: Injected  //OK - injection in class

    process()
      result <- service.execute()

See Also


E08170: Reassignment Of Injected Component

Classification: REASSIGNMENT_OF_INJECTED_COMPONENT

Description

Properties marked for dependency injection (using ! syntax) cannot be directly reassigned using :=. Once a component is injected by the DI container, manually replacing it would break the dependency injection contract and lifecycle management. The DI container maintains control over injected component instances. If you need conditional initialization or want to provide a default, use :=? (guarded/conditional assignment) which only assigns if the variable is still unset after injection attempts.

Example (Error)

#!ek9
defines module bad.injection.examples

  defines component

    Comp1
      default Comp1()

  defines function

    TryToDirectlyReassignInjectedComponent()
      //So this is the injection
      injected as Comp1!

      //But cannot do this - directly reassign use :=? to only assign if null.
      @Error: FULL_RESOLUTION: REASSIGNMENT_OF_INJECTED_COMPONENT
      injected := DefaultComp()

      //Now just ensure it is referenced after a potential assignment (else why did we reassign it)
      assert injected?

Solution

#!ek9
defines class
  ServiceConsumer
    service as IService: Injected

    reconfigure()
      //Use conditional assignment if you need fallback
      service :=? alternativeService  //OK - only if not already set

See Also


E08180: Not Initialized In Any Way

Classification: NOT_INITIALISED_IN_ANY_WAY

Description

A class property is neither initialized at declaration, nor in a constructor, nor marked for dependency injection. EK9 requires all properties to be initialized through one of these three mechanisms.

Example (Error)

#!ek9
defines class
  BadComponent
    @Error: PRE_IR_CHECKS: NOT_INITIALISED_IN_ANY_WAY
    data as String?  //Not initialized anywhere

    process()
      result <- data.length()

Solutions

Option 1: Initialize at declaration

#!ek9
defines class
  GoodComponent
    data as String: "default"  //Initialized

Option 2: Initialize in constructor

#!ek9
defines class
  GoodComponent
    data as String?

    default GoodComponent()
      data: "initialized"

Option 3: Use dependency injection

#!ek9
defines class
  GoodComponent
    data as IDataProvider: Injected  //Injected

See Also


Phase 09: PLUGIN_RESOLUTION

Plugin resolution errors occur when the compiler validates plugin integration points.

E09010: Inappropriate Use

Classification: INAPPROPRIATE_USE

Description

A language construct is being used in a context where it's not appropriate or allowed. This is a general error that covers various misuses of EK9 constructs in inappropriate contexts.

Example (Error)

#!ek9
defines module example

  @Error: PLUGIN_RESOLUTION: INAPPROPRIATE_USE
  result <- this  //Using 'this' outside of a class/method context

Solution

#!ek9
defines module example

  defines class
    Example
      getValue()
        <- result as String: $"Value from ${this}"  //OK - 'this' in class method

See Also


Phase 10: IR_GENERATION

IR generation errors occur during the intermediate representation creation phase, when the compiler translates validated source code into its internal representation.

E10010: Return Type Void Meaningless

Classification: RETURN_TYPE_VOID_MEANINGLESS

Description

A function or method declares a void return type but attempts to assign the result to a variable. Void functions don't return values, so assignment is meaningless.

Example (Error)

#!ek9
processData()
  //No return value declared (implicitly void)
  stdout.println("Processing")

demo()
  @Error: IR_GENERATION: RETURN_TYPE_VOID_MEANINGLESS
  result <- processData()  //Cannot assign void result

Solution

#!ek9
processData()
  <- result as String: "Processing complete"  //Declare return type

demo()
  result <- processData()  //OK - function returns a value

See Also


E10020: Stream Type Not Defined

Classification: STREAM_TYPE_NOT_DEFINED

Description

Void cannot be used in stream pipelines. Each stage of a stream must produce a value that can be passed to the next stage. Void-returning functions break the pipeline.

Example (Error)

#!ek9
processItem()
  -> item as String
  stdout.println(item)  //No return value (void)

demo()
  items <- ["one", "two", "three"]

  @Error: IR_GENERATION: STREAM_TYPE_NOT_DEFINED
  cat items | map with processItem  //Void breaks pipeline

Solution

#!ek9
processItem()
  -> item as String
  <- result as String: item.toUpperCase()  //Return transformed value

demo()
  items <- ["one", "two", "three"]

  cat items | map with processItem  //OK - returns String

See Also


E10030: Constructor Used On Abstract Type

Classification: CONSTRUCTOR_USED_ON_ABSTRACT_TYPE

Description

Direct instantiation of an abstract type (trait, abstract class) using a constructor is not permitted. Abstract types must be extended/implemented by concrete types, which are then instantiated.

Example (Error)

#!ek9
defines trait
  Processable
    process()
      <- result as String?

demo()
  @Error: IR_GENERATION: CONSTRUCTOR_USED_ON_ABSTRACT_TYPE
  obj <- Processable()  //Cannot instantiate trait directly

Solution

#!ek9
defines trait
  Processable
    process()
      <- result as String?

defines class
  ConcreteProcessor with trait of Processable
    override process()
      <- result as String: "Processed"

demo()
  obj <- ConcreteProcessor()  //OK - instantiate concrete implementation

See Also


Phase 11: IR_ANALYSIS

IR analysis errors are detected during complexity analysis and code quality checks on the intermediate representation.

E11010: Excessive Complexity

Classification: EXCESSIVE_COMPLEXITY

Description

A function or method has excessive cyclomatic complexity and should be refactored into smaller, more maintainable functions. High complexity makes code difficult to understand, test, and maintain.

Example (Error)

#!ek9
@Error: IR_ANALYSIS: EXCESSIVE_COMPLEXITY
processData()
  -> data as Integer
  <- result as String?

  if data < 0
    if data < -10
      if data < -20
        result: "Very negative"
      else
        result: "Quite negative"
    else
      result: "Slightly negative"
  else
    if data > 10
      if data > 20
        result: "Very positive"
      else
        result: "Quite positive"
    else
      result: "Slightly positive"
  //... many more nested conditions

Solution

#!ek9
classifyNegative()
  -> value as Integer
  <- result as String: value < -20 ? "Very negative" : "Quite negative"

classifyPositive()
  -> value as Integer
  <- result as String: value > 20 ? "Very positive" : "Quite positive"

processData()
  -> data as Integer
  <- result as String: data < 0 ? classifyNegative(data) : classifyPositive(data)

Best Practice: Keep functions focused and simple. If a function becomes complex, extract logical groups into separate helper functions. Aim for cyclomatic complexity < 10 for most functions.

See Also


E11011: Excessive Nesting

Classification: EXCESSIVE_NESTING

Description

A function, method, or operator has excessive nesting depth of control structures. Deep nesting makes code difficult to read, understand, and maintain. The maximum allowed nesting depth is 10 levels.

Example (Error)

#!ek9
@Error: PRE_IR_CHECKS: EXCESSIVE_NESTING
processData()
  -> value as Integer
  <- result as Integer: 0

  if value > 0
    if value > 1
      if value > 2
        if value > 3
          if value > 4
            if value > 5
              if value > 6
                if value > 7
                  if value > 8
                    if value > 9
                      if value > 10  //11th level - exceeds limit!
                        result: 11

Solution

#!ek9
//Extract nested logic into separate functions
processDeepValue()
  -> value as Integer
  <- result as Integer: value > 10 ? 11 : 10

processData()
  -> value as Integer
  <- result as Integer: 0

  if value > 0
    if value > 1
      if value > 2
        if value > 3
          if value > 4
            result: processDeepValue(value)

Best Practice: Keep nesting shallow by extracting deeply nested logic into helper functions. If you find yourself nesting more than 3-4 levels deep, consider refactoring. Guard clauses and early returns (via guard expressions) can also help reduce nesting.

See Also


Common/Multi-Phase Errors (E50xxx)

These errors can be triggered in multiple compilation phases because they represent fundamental issues that EK9 checks throughout the compilation process.

E50001: Not Resolved

Classification: NOT_RESOLVED

Type: Common (Multi-Phase)

Can occur in phases:

Description

A symbol (variable, type, function, method, etc.) could not be found in the current scope. The specific context depends on which compilation phase detected the error.

Example (Error)

#!ek9
defines module example

  demo()
    @Error: REFERENCE_CHECKS: NOT_RESOLVED
    result <- unknownVariable  //This variable doesn't exist

Solution

#!ek9
defines module example

  demo()
    myVariable <- "value"  //Declare first
    result <- myVariable  //Now it resolves

Common Causes

See Also


E50010: Type Not Resolved

Classification: TYPE_NOT_RESOLVED

Type: Common (Multi-Phase)

Can occur in phases:

Description

A type (class, record, function, trait, etc.) could not be found or resolved. This often happens when referencing a type that hasn't been defined or imported.

Example (Error)

#!ek9
defines module example

  demo()
    @Error: TYPE_NOT_RESOLVED
    myObject as UnknownType?  //This type doesn't exist

Solution

#!ek9
defines module example

  defines record
    MyType
      value as Integer?

  demo()
    myObject as MyType?  //Now it resolves

See Also


E50020: Incompatible Genus

Classification: INCOMPATIBLE_GENUS

Type: Common (Multi-Phase)

Description

An operation requires a specific genus (type category) but received a different one. EK9 has distinct genus categories: CLASS, RECORD, TRAIT, FUNCTION, COMPONENT, SERVICE, etc. Operations designed for one genus cannot accept another.

Example (Error)

#!ek9
defines function
  MyFunction()
    <- result as String: "function result"

defines class
  MyClass
    process()  //Expecting class genus
      -> fn as MyClass
      <- result as String: "processed"

demo()
  @Error: INCOMPATIBLE_GENUS
  obj <- MyClass()
  result <- obj.process(MyFunction)  //Passing function to class parameter

Solution

#!ek9
defines class
  MyClass
    process()  //Use matching genus
      -> other as MyClass
      <- result as String: "processed"

demo()
  obj1 <- MyClass()
  obj2 <- MyClass()
  result <- obj1.process(obj2)  //Both are class genus

See Also


E50030: Incompatible Types

Classification: INCOMPATIBLE_TYPES

Type: Common (Multi-Phase)

Description

Two types are incompatible and cannot be used together in the given context. This includes assignment incompatibility, parameter type mismatches, and return type conflicts.

Example (Error)

#!ek9
defines module example

  demo()
    stringValue <- "text"
    @Error: INCOMPATIBLE_TYPES
    numberValue as Integer: stringValue  //Cannot assign String to Integer

Solution

#!ek9
defines module example

  demo()
    stringValue <- "42"
    numberValue as Integer: Integer(stringValue)  //Convert explicitly

Common Causes

See Also


E50040: Cannot Be Abstract

Classification: CANNOT_BE_ABSTRACT

Type: Common (Multi-Phase)

Description

A construct has been marked as abstract in a context where abstract is not allowed. Only specific constructs (classes, functions, methods) can be abstract, and only in appropriate contexts.

Example (Error)

#!ek9
defines module example

  @Error: CANNOT_BE_ABSTRACT
  defines record as abstract  //Records cannot be abstract
    MyRecord
      value as Integer?

Solution

#!ek9
defines module example

  defines record  //Remove abstract modifier
    MyRecord
      value as Integer?

Abstract Rules

See Also


E50050: Duplicate Variable

Classification: DUPLICATE_VARIABLE

Type: Common (Multi-Phase)

Description

A variable or constant has been declared multiple times in the same scope. EK9 does not allow redeclaration of variables within the same scope.

Example (Error)

#!ek9
defines module example

  demo()
    value <- 42
    @Error: DUPLICATE_VARIABLE
    value <- 100  //Redeclaration with <- not allowed

Solution 1: Use Assignment Operator

#!ek9
defines module example

  demo()
    value <- 42  //Declaration
    value := 100  //Assignment (use := not <-)

Solution 2: Use Different Name

#!ek9
defines module example

  demo()
    originalValue <- 42
    newValue <- 100  //Different variable

See Also


E50060: Method Not Resolved

Classification: METHOD_NOT_RESOLVED

Type: Common (Multi-Phase)

Description

A method or function call could not be resolved. This typically means no matching method/function exists with the given name and parameter types.

Example (Error)

#!ek9
defines class
  MyClass
    doSomething()
      -> value as Integer
      <- result as String: $value

demo()
  obj <- MyClass()
  @Error: METHOD_NOT_RESOLVED
  result <- obj.doSomethingElse()  //Method doesn't exist

Solution

#!ek9
defines class
  MyClass
    doSomething()
      -> value as Integer
      <- result as String: $value

demo()
  obj <- MyClass()
  result <- obj.doSomething(42)  //Call existing method

Common Causes

See Also


E50070: Bad Abstract Function Use

Classification: BAD_ABSTRACT_FUNCTION_USE

Type: Common (Multi-Phase)

Description

An abstract function is being used in a way that's not permitted. Abstract functions can only be used in specific contexts - they cannot be called directly and must be implemented or passed as higher-order function references.

Example (Error)

#!ek9
defines function as abstract
  Processor
    -> input as String
    <- output as String

demo()
  @Error: BAD_ABSTRACT_FUNCTION_USE
  result <- Processor("test")  //Cannot call abstract function directly

Solution

#!ek9
defines function as abstract
  Processor
    -> input as String
    <- output as String

defines function as Processor
  UpperCaseProcessor
    -> input as String
    <- output as String: input.upperCase()

demo()
  result <- UpperCaseProcessor("test")  //Call concrete implementation

See Also


E50080: Cannot Call Abstract Type

Classification: CANNOT_CALL_ABSTRACT_TYPE

Type: Common (Multi-Phase)

Description

Attempted to instantiate or call an abstract class, function, or method directly. Abstract types must be extended/implemented before they can be used.

Example (Error)

#!ek9
defines class as abstract
  Shape
    abstract area()
      <- result as Float

demo()
  @Error: CANNOT_CALL_ABSTRACT_TYPE
  shape <- Shape()  //Cannot instantiate abstract class

Solution

#!ek9
defines class as abstract
  Shape
    abstract area()
      <- result as Float

defines class as open extends Shape
  Circle
    radius <- 5.0

    override area()
      <- result as Float: 3.14159 * radius * radius

demo()
  shape <- Circle()  //Instantiate concrete class

See Also


E50090: Incompatible Parameter Genus

Classification: INCOMPATIBLE_PARAMETER_GENUS

Type: Common (Multi-Phase)

Description

One or more function/method parameters have incompatible genus (type category). When calling a function, all arguments must match the genus of the corresponding parameters.

Example (Error)

#!ek9
defines record
  DataRecord
    value as Integer?

defines function
  process()
    -> record as DataRecord
    <- result as String: $record.value

defines class
  DataClass
    value as Integer: 0

demo()
  data <- DataClass()
  @Error: INCOMPATIBLE_PARAMETER_GENUS
  result <- process(data)  //Passing CLASS to RECORD parameter

Solution

#!ek9
defines record
  DataRecord
    value as Integer?

defines function
  process()
    -> record as DataRecord
    <- result as String: $record.value

demo()
  data <- DataRecord(42)
  result <- process(data)  //Matching genus

See Also


E50100: Incompatible Category

Classification: INCOMPATIBLE_CATEGORY

Type: Common (Multi-Phase)

Description

A symbol has an incompatible category for the operation being performed. Categories include METHOD, VARIABLE, FUNCTION, TYPE, etc. Each context requires specific categories.

Example (Error)

#!ek9
defines class
  Calculator
    add()
      ->
        a as Integer
        b as Integer

demo()
  calc <- Calculator()
  @Error: INCOMPATIBLE_CATEGORY
  methodRef <- calc.add  //Cannot reference method without call context

Solution

#!ek9
defines function
  add()
    ->
      a as Integer
      b as Integer

demo()
  functionRef <- add  //Functions can be referenced
  result <- functionRef(5, 3)

See Also


Testing Directives (E50200-E50310)

These errors relate to EK9's testing directive system. Directives like @Error, @Resolved, @IR, and @Complexity are used in test files to validate compiler behavior. These errors indicate mismatches between expected and actual compiler behavior.

E50200: Unknown Directive

Classification: UNKNOWN_DIRECTIVE

Type: Testing Infrastructure

Description

An unrecognized directive was encountered in test code. Valid directives include @Error, @Resolved, @IR, @BYTECODE, and @Complexity.

Example (Error)

#!ek9
defines module test

  demo()
    @Error: UNKNOWN_DIRECTIVE
    @InvalidDirective: SOME_ERROR  //Not a valid directive
    value <- unknownVariable

Solution

#!ek9
defines module test

  demo()
    @Error: NOT_RESOLVED  //Use valid directive
    value <- unknownVariable

Valid Directives

See Also


E50210: Directive Missing

Classification: DIRECTIVE_MISSING

Type: Testing Infrastructure

Description

The compiler expected a directive but none was found. This typically occurs during test validation when the test framework expects specific markers.

Example (Error)

#!ek9
//Test expects @Error directive but it's missing
defines module test

  demo()
    @Error: DIRECTIVE_MISSING
    value <- unknownVariable  //Should have @Error directive on this line

Solution

#!ek9
defines module test

  demo()
    @Error: NOT_RESOLVED  //Add expected directive
    value <- unknownVariable

See Also


E50220: Directive Wrong Classification

Classification: DIRECTIVE_WRONG_CLASSIFICATION

Type: Testing Infrastructure

Description

An @Error directive specified the wrong error classification. The actual error generated by the compiler doesn't match the expected classification.

Example (Error)

#!ek9
defines module test

  demo()
    @Error: DIRECTIVE_WRONG_CLASSIFICATION
    @Error: TYPE_MISMATCH  //Wrong classification
    value <- unknownVariable  //Actually generates NOT_RESOLVED

Solution

#!ek9
defines module test

  demo()
    @Error: NOT_RESOLVED  //Correct classification
    value <- unknownVariable

Tip: Run the compiler without directives first to see what actual error is generated, then add the matching @Error directive.

See Also


E50230: Error Missing

Classification: ERROR_MISSING

Type: Testing Infrastructure

Description

An @Error directive was specified but the compiler did not generate the expected error. The code compiled successfully when it was expected to fail.

Example (Error)

#!ek9
defines module test

  demo()
    @Error: ERROR_MISSING
    @Error: NOT_RESOLVED
    value <- "valid code"  //This is valid, no error generated

Solution

#!ek9
defines module test

  demo()
    value <- "valid code"  //Remove @Error directive for valid code

  testError()
    @Error: NOT_RESOLVED
    value <- unknownVariable  //Actually generates expected error

See Also


E50240: Directive Symbol Complexity

Classification: DIRECTIVE_SYMBOL_COMPLEXITY

Type: Testing Infrastructure

Description

A @Complexity directive specified a different complexity value than what the compiler calculated for the function/method.

Example (Error)

#!ek9
defines module test

  @Error: DIRECTIVE_SYMBOL_COMPLEXITY
  @Complexity: 5  //Wrong complexity value
  simpleFunction()
    <- result as String: "simple"  //Actual complexity is 1

Solution

#!ek9
defines module test

  @Complexity: 1  //Correct complexity
  simpleFunction()
    <- result as String: "simple"

See Also


E50250: Directive Symbol Not Resolved

Classification: DIRECTIVE_SYMBOL_NOT_RESOLVED

Type: Testing Infrastructure

Description

A @Resolved directive expected a symbol to be resolved, but it wasn't. This validates that symbol resolution is working correctly in test code.

Example (Error)

#!ek9
defines module test

  demo()
    @Error: DIRECTIVE_SYMBOL_NOT_RESOLVED
    @Resolved: SYMBOL_DEFINITION
    value <- unknownVariable  //Symbol not resolved as expected

Solution

#!ek9
defines module test

  demo()
    value <- 42  //Define symbol first
    @Resolved: SYMBOL_DEFINITION
    result <- value  //Now symbol resolves

See Also


E50260: Directive Hierarchy Not Resolved

Classification: DIRECTIVE_HIERARCHY_NOT_RESOLVED

Type: Testing Infrastructure

Description

A directive expected a type hierarchy to be resolved (inheritance, trait implementation) but the hierarchy was not properly established.

Example (Error)

#!ek9
defines module test

  @Error: DIRECTIVE_HIERARCHY_NOT_RESOLVED
  defines class extends UnknownBase  //Base not resolved
    @Resolved: TYPE_HIERARCHY_CHECKS
    MyClass
      value as Integer?

Solution

#!ek9
defines module test

  defines class
    BaseClass
      value as Integer?

  defines class extends BaseClass
    @Resolved: TYPE_HIERARCHY_CHECKS
    MyClass
      extraValue as String?

See Also


E50270: Directive Symbol Category Mismatch

Classification: DIRECTIVE_SYMBOL_CATEGORY_MISMATCH

Type: Testing Infrastructure

Description

A directive validation expected a specific symbol category (METHOD, VARIABLE, FUNCTION, TYPE) but found a different category.

Example (Error)

#!ek9
defines module test

  @Error: DIRECTIVE_SYMBOL_CATEGORY_MISMATCH
  @IR: SYMBOL_DEFINITION: METHOD: myFunction  //Wrong category
  myFunction()  //This is a FUNCTION not METHOD
    <- result as String: "test"

Solution

#!ek9
defines module test

  @IR: SYMBOL_DEFINITION: FUNCTION: myFunction  //Correct category
  myFunction()
    <- result as String: "test"

See Also


E50280: Directive Symbol Genus Mismatch

Classification: DIRECTIVE_SYMBOL_GENUS_MISMATCH

Type: Testing Infrastructure

Description

A directive validation expected a specific genus (CLASS, RECORD, FUNCTION, etc.) but the symbol has a different genus.

Example (Error)

#!ek9
defines module test

  @Error: DIRECTIVE_SYMBOL_GENUS_MISMATCH
  @IR: SYMBOL_DEFINITION: CLASS: MyType  //Wrong genus
  defines record  //This is RECORD genus
    MyType
      value as Integer?

Solution

#!ek9
defines module test

  @IR: SYMBOL_DEFINITION: RECORD: MyType  //Correct genus
  defines record
    MyType
      value as Integer?

See Also


E50290: Directive Symbol No Such Genus

Classification: DIRECTIVE_SYMBOL_NO_SUCH_GENUS

Type: Testing Infrastructure

Description

A directive referenced a genus that doesn't exist in EK9's type system.

Example (Error)

#!ek9
defines module test

  @Error: DIRECTIVE_SYMBOL_NO_SUCH_GENUS
  @IR: SYMBOL_DEFINITION: INTERFACE: MyType  //INTERFACE is not an EK9 genus
  defines class
    MyType
      value as Integer?

Solution

#!ek9
defines module test

  @IR: SYMBOL_DEFINITION: CLASS: MyType  //Use valid genus
  defines class
    MyType
      value as Integer?

Valid EK9 Genus Types

See Also


E50300: Directive Symbol Found Unexpected Symbol

Classification: DIRECTIVE_SYMBOL_FOUND_UNEXPECTED_SYMBOL

Type: Testing Infrastructure

Description

A directive validation expected a specific symbol but found a different one. This ensures test directives are validating the correct symbols.

Example (Error)

#!ek9
defines module test

  @Error: DIRECTIVE_SYMBOL_FOUND_UNEXPECTED_SYMBOL
  @IR: SYMBOL_DEFINITION: FUNCTION: wrongName  //Wrong symbol name
  correctName()  //Symbol name doesn't match directive
    <- result as String: "test"

Solution

#!ek9
defines module test

  @IR: SYMBOL_DEFINITION: FUNCTION: correctName  //Matching symbol name
  correctName()
    <- result as String: "test"

See Also


E50310: Directive Error Mismatch

Classification: DIRECTIVE_ERROR_MISMATCH

Type: Testing Infrastructure

Description

The number of errors generated by the compiler doesn't match the number of @Error directives in the test file.

Example (Error)

#!ek9
defines module test

  demo()
    @Error: DIRECTIVE_ERROR_MISMATCH
    @Error: NOT_RESOLVED
    value1 <- unknownVar1  //1 @Error directive
    value2 <- unknownVar2  //But 2 actual errors!

Solution

#!ek9
defines module test

  demo()
    @Error: NOT_RESOLVED
    value1 <- unknownVar1  //1 @Error directive
    @Error: NOT_RESOLVED
    value2 <- unknownVar2  //2 @Error directives matching 2 errors

See Also


Phase 06: TYPE_HIERARCHY_CHECKS

Type hierarchy checks validate generic/template types, method resolution, trait implementation, and parameter matching. This phase ensures type hierarchies are correctly structured and all type constraints are satisfied.

E06010: Generic Type Or Function Parameters Needed

Classification: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_NEEDED

Description

A generic/template type or function requires type parameters but none were provided. In EK9, generic types like List, Optional, or Dict, and generic functions with of type declarations, must be explicitly parameterized when instantiated. Type inference is only supported when constructor arguments provide sufficient type information (e.g., List("Steve") infers List of String).

Example (Error)

#!ek9
defines module bad.functioncall.examples1

  defines function

    GenericFunction of type (S, T)
      ->
        arg0 as S
        arg1 as T
      <-
        rtn as Boolean: true

    TestInvalidGenericFunctionUse1()
      //Detect this is as being generic but omit polymorphic parameters and ensure we get an error
      @Error: FULL_RESOLUTION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_NEEDED
      result4A <- GenericFunction()

      assert result4A?

    TestInvalidGenericAggregateUse1()
      @Error: FULL_RESOLUTION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_NEEDED
      result5A <- List()

      assert result5A?

Solution

#!ek9
defines module bad.functioncall.examples1

  defines function

    GenericFunction of type (S, T)
      ->
        arg0 as S
        arg1 as T
      <-
        rtn as Boolean: true

    TestValidGenericFunctionUse()
      //Provide explicit type parameters when calling generic function
      result <- GenericFunction("Steve", 21)
      assert result?

    TestValidGenericAggregateUse()
      //Provide type parameter or use constructor argument for inference
      result1 <- List() of String  //Explicit parameterization
      result2 <- List("Steve")     //Type inference from argument
      assert result1? and result2?

See Also


E06020: Generic Type Or Function Parameters Incorrect

Classification: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT

Description

The number of type parameters provided to a generic type or function does not match the required count. Each generic type has specific parameter requirements: List and Optional require exactly 1 type parameter, while Dict requires exactly 2 (key and value types). This error is detected during symbol definition when the compiler can determine parameter count mismatches from explicit type declarations.

Example (Error)

#!ek9
defines module incorrect.generic.uses

  defines function

    invalidNumberOfParametersExplicitType1()
      //List requires 1 type parameter, given 2
      @Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
      val4 as List of (Integer, Date): List()

      //Dict requires 2 type parameters, given 1
      @Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
      dict4 as Dict of (Integer): Dict()

    invalidNumberOfParametersExplicitType2()
      //List requires 1 type parameter, given 2
      @Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
      val5 <- List() of (Integer, Date)

      //Dict requires 2 type parameters, given 1
      @Error: SYMBOL_DEFINITION: GENERIC_TYPE_OR_FUNCTION_PARAMETERS_INCORRECT
      dict5 <- Dict() of (Integer)

Solution

#!ek9
defines module incorrect.generic.uses

  defines function

    validNumberOfParameters()
      //List requires exactly 1 type parameter
      val4 as List of Integer: List()
      val5 <- List() of String

      //Dict requires exactly 2 type parameters (key, value)
      dict4 as Dict of (Integer, String): Dict()
      dict5 <- Dict() of (String, Date)

See Also


E06030: Generic Type Constructor Inappropriate

Classification: GENERIC_TYPE_CONSTRUCTOR_INAPPROPRIATE

Description

A constructor defined in a generic/template type has an inappropriate parameter signature for type inference. EK9 requires that constructors in generic types either have no parameters (default constructor) or have exactly one parameter of each type variable for type inference. A constructor with multiple parameters of the same type variable prevents the compiler from inferring the concrete type and is therefore inappropriate.

Example (Error)

#!ek9
defines module incorrect.parameters.on.constructors

  defines class

    SomeGenericType of type T
      aField as T?

      default SomeGenericType()

      SomeGenericType()
        -> param as T
        aField :=: param

      //This is not allowed, as it is expected a single parameter will give the compiler
      //the type to infer for the generic nature of the generic/template class.
      @Error: SYMBOL_DEFINITION: GENERIC_TYPE_CONSTRUCTOR_INAPPROPRIATE
      SomeGenericType()
        ->
          param1 as T
          param2 as T
        aField :=: param1 + param2

Solution

#!ek9
defines module incorrect.parameters.on.constructors

  defines class

    SomeGenericType of type T
      aField as T?

      //Default constructor (no parameters)
      default SomeGenericType()

      //Constructor with single parameter for type inference
      SomeGenericType()
        -> param as T
        aField :=: param

See Also


E06040: Generic Type Requires Two Constructors

Classification: GENERIC_TYPE_REQUIRES_TWO_CONSTRUCTORS

Description

When defining constructors for generic/template types, EK9 requires either no explicit constructors (allowing the compiler to synthesize both default and parameterized constructors) or exactly two explicit constructors: a default constructor with no parameters and a constructor accepting one parameter for each type variable. Defining only one constructor creates ambiguity and prevents proper type inference, so it is not allowed.

Example (Error)

#!ek9
defines module bad.genericnotparameterised.example

  defines class

    
    @Error: SYMBOL_DEFINITION: GENERIC_TYPE_REQUIRES_TWO_CONSTRUCTORS
    C3 of type T
      default C3()

      
      check()
        -> arg0 as T
        @Error: SYMBOL_DEFINITION: TYPE_INFERENCE_NOT_SUPPORTED
        <- rtn <- arg0?

Solution

#!ek9
defines module bad.genericnotparameterised.example

  defines class

    //Option 1: Let compiler synthesize both constructors (no explicit constructors)
    C3Auto of type T
      check()
        -> arg0 as T
        <- rtn as Boolean: arg0?

    //Option 2: Define both constructors explicitly
    C3Manual of type T
      default C3Manual()

      C3Manual()
        -> arg0 as T

      check()
        -> arg0 as T
        <- rtn as Boolean: arg0?

See Also


E06050: Generic Type Requires Correct Constructor Argument Types

Classification: GENERIC_TYPE_REQUIRES_CORRECT_CONSTRUCTOR_ARGUMENT_TYPES

Description

The type-inferred constructor of a generic type must use the correct type parameters in the correct order. For a generic type declared as of type (S, T), the parameterized constructor must accept arguments (arg0 as S, arg1 as T) in that exact order. Using the wrong type parameter (e.g., T instead of S), using non-existent type parameters, or placing them in the wrong order will prevent proper type inference.

Example (Error)

#!ek9
defines module bad.genericnotparameterised.example

  defines class

    C8 of type (S, T)

      default C8()

      default C8()
        ->
          //Wrong: both parameters are T, should be (S, T)
          @Error: SYMBOL_DEFINITION: GENERIC_TYPE_REQUIRES_CORRECT_CONSTRUCTOR_ARGUMENT_TYPES
          arg0 as T
          arg1 as T

      check()
        -> arg0 as T
        <- rtn as Boolean: arg0?

Solution

#!ek9
defines module bad.genericnotparameterised.example

  defines class

    C8Fixed of type (S, T)

      default C8Fixed()

      default C8Fixed()
        ->
          arg0 as S  //Correct: first parameter uses S
          arg1 as T  //Correct: second parameter uses T

      check()
        -> arg0 as T
        <- rtn as Boolean: arg0?

See Also


E06060: Generic Constructors Must Be Public

Classification: GENERIC_CONSTRUCTORS_MUST_BE_PUBLIC

Description

Generic/template types do not support private or protected constructors. All constructors in generic types must be public (the default visibility) to allow proper parameterization and instantiation. This restriction ensures that generic types can be properly instantiated with concrete type parameters from any context.

Example (Error)

#!ek9
defines module bad.genericnotparameterised.example

  defines class

    
    C4 of type T
      @Error: SYMBOL_DEFINITION: GENERIC_CONSTRUCTORS_MUST_BE_PUBLIC
      default private C4()

      @Error: SYMBOL_DEFINITION: GENERIC_CONSTRUCTORS_MUST_BE_PUBLIC
      default private C4()
        -> arg0 as T

      check()
        -> arg0 as T
        <- rtn as Boolean: arg0?

Solution

#!ek9
defines module bad.genericnotparameterised.example

  defines class

    C4Fixed of type T
      //Public by default (no access modifier)
      default C4Fixed()

      default C4Fixed()
        -> arg0 as T

      check()
        -> arg0 as T
        <- rtn as Boolean: arg0?

See Also


E06070: Type Inference Not Supported

Classification: TYPE_INFERENCE_NOT_SUPPORTED

Description

Type inference using the <- operator is not supported within generic/template type or function definitions. This restriction applies to all variable declarations inside generic contexts, regardless of whether they use the generic type parameter (T) or concrete types like Integer. All types must be explicitly declared using as TypeName syntax to ensure type safety and clarity within generic code.

Example (Error)

#!ek9
defines module bad.inference.example

  defines class

    AGenericClass of type T
      item as T?

      default AGenericClass()

      AGenericClass()
        -> arg0 as T
        item :=: arg0

      //No inference allowed at all not just with conceptual T
      notOkToUse()
        <- rtn as Iterator of Integer?
        @Error: SYMBOL_DEFINITION: TYPE_INFERENCE_NOT_SUPPORTED
        value <- 1
        @Error: SYMBOL_DEFINITION: TYPE_INFERENCE_NOT_SUPPORTED
        someVar <- Iterator(value)
        rtn: someVar

Solution

#!ek9
defines module bad.inference.example

  defines class

    AGenericClass of type T
      item as T?

      default AGenericClass()

      AGenericClass()
        -> arg0 as T
        item :=: arg0

      okToUse()
        <- rtn as Iterator of Integer?
        value as Integer: 1  //Explicit type declaration
        someVar as Iterator of Integer: Iterator(value)
        rtn: someVar

See Also


E06080: Constrained Functions Not Supported

Classification: CONSTRAINED_FUNCTIONS_NOT_SUPPORTED

Description

When constraining a generic type parameter using the constrain by clause, the constraining type must be a class, trait, component, record, or type. Functions, text constructs, and constants cannot be used as type constraints because they do not define structural requirements that can be verified at the type level.

Example (Error)

#!ek9
defines module bad.generic.constraining.types

  defines function

    CheckFunction()
      -> arg0 as String
      <- rtn <- true

    AbstractFunction() as abstract
      -> arg0 as String
      <- rtn as Boolean?

  defines class

    
    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: CONSTRAINED_FUNCTIONS_NOT_SUPPORTED
    BadGeneric1 of type T constrain by CheckFunction

      default BadGeneric1()

      BadGeneric1()
        -> arg0 as T
        assert arg0?

      check()
        -> arg0 as T
        <- rtn as Boolean: true

    @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: CONSTRAINED_FUNCTIONS_NOT_SUPPORTED
    BadGeneric2 of type T constrain by AbstractFunction

      default BadGeneric2()

      BadGeneric2()
        -> arg0 as T
        assert arg0?

      check()
        -> arg0 as T
        <- rtn as Boolean: true

Solution

#!ek9
defines module bad.generic.constraining.types

  defines trait

    T1
      check()
        -> arg0 as String
        <- rtn <- true

  defines component
    Comp1 as open
      check()
        -> arg0 as String
        <- rtn <- true

  defines class

    
    OKGeneric1 of type T constrain by T1
      default OKGeneric1()

      OKGeneric1()
        -> arg0 as T
        assert arg0?

      check()
        -> arg0 as T
        <- rtn as Boolean: true

    
    OKGeneric2 of type T constrain by Comp1
      default OKGeneric2()

      OKGeneric2()
        -> arg0 as T
        assert arg0?

      check()
        -> arg0 as T
        <- rtn as Boolean: true

See Also


E06090: Generic With Named Dynamic Class

Classification: GENERIC_WITH_NAMED_DYNAMIC_CLASS

Description

Named dynamic classes cannot be used within generic type or function definitions. EK9 prohibits naming dynamic classes in generic contexts to avoid naming conflicts and complexity issues that would arise during generic type instantiation. Dynamic classes within generics must be anonymous (unnamed) to ensure each parameterized instance maintains independent class definitions.

Example (Error)

#!ek9
defines module fuzz.dynamic.generic.type.named

  defines trait
    T1
      method1()
        -> arg0 as String
        <- rtn as String: arg0

  defines class

    GenericContainer of type T
      process()
        -> item as T

        //This is INVALID - named dynamic class inside generic type
        @Error: SYMBOL_DEFINITION: GENERIC_WITH_NAMED_DYNAMIC_CLASS
        dynamic as T1 := NamedDynamic() with trait of T1 as class
          override method1()
            -> arg0 as String
            <- rtn as String: "processed: " + arg0

        assert dynamic?

Solution

#!ek9
defines module fuzz.dynamic.generic.type.named

  defines trait
    T1
      method1()
        -> arg0 as String
        <- rtn as String: arg0

  defines class

    GenericContainer of type T
      process()
        -> item as T

        //Anonymous dynamic class (no name) - this is allowed
        dynamic as T1 := () with trait of T1 as class
          override method1()
            -> arg0 as String
            <- rtn as String: "processed: " + arg0

        assert dynamic?

See Also


E06100: Generic Function Implementation Required

Classification: GENERIC_FUNCTION_IMPLEMENTATION_REQUIRED

Description

When creating a dynamic function from an abstract generic function, an implementation body must be provided. Abstract generic functions cannot be used directly without implementing their abstract behavior. This error occurs when attempting to create a dynamic function instance using is AbstractGenericFunction as function syntax without providing the required method implementation.

Example (Error)

#!ek9
defines module generic.function.declaration.only

  defines function

    //Define an abstract generic function with type parameter T
    AbstractProcessor() of type T as abstract
      -> input as T
      <- result as T?

  defines program

    TestInvalidGenericFunctionUsage()
      //Try to use abstract generic function without providing implementation
      //This should fail because the function needs an implementation body
      @Error: FULL_RESOLUTION: GENERIC_FUNCTION_IMPLEMENTATION_REQUIRED
      processor <- () is AbstractProcessor of String as function

      assert processor?

Solution

#!ek9
defines module generic.function.declaration.only

  defines function

    AbstractProcessor() of type T as abstract
      -> input as T
      <- result as T?

  defines program

    TestValidGenericFunctionUsage()
      //Provide implementation for the abstract generic function
      processor <- () is AbstractProcessor of String as function
        -> input as String
        <- result as String: input + " processed"

      assert processor?

See Also


E06110: Constructor With Function In Generic

Classification: CONSTRUCTOR_WITH_FUNCTION_IN_GENERIC

Description

When a generic type is parameterized with a function type, the generic cannot use operations that assume constructors. Functions in EK9 do not have constructors in the same way classes do (using T() syntax). This error occurs when a generic type attempts to call T() assuming a constructor, but T is bound to a function type. Generic types can contain functions (as with Optional or List), but cannot invoke function constructors.

Example (Error)

#!ek9
defines module bad.generic.constraining.resolution2

  defines function
    CheckFunction()
      -> arg0 as Integer
      <- rtn as Boolean: arg0 > 21

  defines class

    UnconstrainedGeneric1 of type T

      default UnconstrainedGeneric1()

      default UnconstrainedGeneric1()
        -> arg0 as T

      check()
        ->
          arg0 as T
          arg1 as T
        <-
          rtn as Integer: arg0 <=> arg1

      createNew()
        <- rtn as T: T()  //Assumes constructor exists

  defines function

    //This generic type assumes the possibility of a constructor
    ExampleOfInvalidTypeUse4()
      @Error: POST_RESOLUTION_CHECKS: CONSTRUCTOR_WITH_FUNCTION_IN_GENERIC
      demo <- UnconstrainedGeneric1() of CheckFunction
      result <- demo.check(CheckFunction, CheckFunction)
      assert result == 0

Solution

#!ek9
defines module bad.generic.constraining.resolution2

  defines function
    CheckFunction()
      -> arg0 as Integer
      <- rtn as Boolean: arg0 > 21

  defines class

    //This generic type can work with functions because it only uses '?'
    UnconstrainedGeneric2 of type T

      default UnconstrainedGeneric2()

      default UnconstrainedGeneric2()
        -> arg0 as T

      check()
        ->
          arg0 as T
          arg1 as T
        <-
          rtn as Boolean: arg0? and arg1?  //Only uses is-set operator

  defines function

    ExampleOfValidTypeUse5()
      demo <- UnconstrainedGeneric2() of CheckFunction
      result <- demo.check(CheckFunction, CheckFunction)
      assert result

See Also


E06120: Function Used In Generic

Classification: FUNCTION_USED_IN_GENERIC

Description

When a generic type is parameterized with a function type, the generic cannot use operators that are not applicable to functions. Functions in EK9 support only the is-set operator (?) and cannot be used with operators like addition (+), comparison (<=>), or other operators that assume non-function types. Generic types can contain functions, but only if they restrict operations to those supported by function types.

Example (Error)

#!ek9
defines module bad.generic.constraining.resolution3

  defines function

    AbstractFunction() as abstract
      -> arg0 as String
      <- rtn as Boolean?

    //Unconstrained generic function that uses the addition operator
    GF1 of type T
      ->
        arg0 as T
        arg1 as T
      <-
        rtn as T: arg0 + arg1

  defines function

    //This generic assumes '+' operator, but functions don't have '+'
    GF1CannotBeUsedWithFunctions()
      @Error: POST_RESOLUTION_CHECKS: FUNCTION_USED_IN_GENERIC
      gf1 <- GF1() of AbstractFunction
      assert gf1?

Solution

#!ek9
defines module bad.generic.constraining.resolution3

  defines function

    AbstractFunction() as abstract
      -> arg0 as String
      <- rtn as Boolean?

    ConcreteFunction is AbstractFunction
      -> arg0 as String
      <- rtn <- true

    //Generic function using only is-set operator (works with functions)
    GF3 of type T
      ->
        arg0 as T
        arg1 as T
      <- rtn as T: arg0? <- arg0 : arg1

  defines function

    GF3CanBeUsedWithFunctions()
      -> possiblyUnSetFunction as AbstractFunction

      gf3 <- GF3() of AbstractFunction

      //Use a normal function and a dynamic function
      func <- gf3(possiblyUnSetFunction, () is AbstractFunction (rtn: false))
      assert func?

See Also


E06130: Constrained Type Constructor Missing

Classification: CONSTRAINED_TYPE_CONSTRUCTOR_MISSING

Description

When a generic type or function is constrained using constrain by, the type used to parameterize the generic must provide all constructors that exist on the constraining type. EK9 enforces constructor completeness to ensure that generic code can safely instantiate new instances using T(constrainingTypeConstructorSignature). If the parameterizing type is missing any constructor that the constraining type defines, this error is raised.

Example (Error)

#!ek9
defines module bad.generic.constraining.resolution3

  defines record

    R2 as open
      value as Integer?

      R2() as pure
        value :=? 1

      R2() as pure
        -> arg0 as Integer
        value :=? Integer(arg0)

      R2() as pure
        -> arg0 as R2
        value :=? Integer(arg0.value)

      operator + as pure
        -> arg0 as R2
        <- rtn as R2: R2(value + arg0.value)

      default operator ?

    R3 is R2
      anotherValue as Integer?

      R3() as pure
        value :=? 1

      R3() as pure
        -> arg0 as Integer
        super(arg0)

      //Missing: R3(-> arg0 as R2) constructor

  defines function

    GF2 of type T constrain by R2
      ->
        arg0 as T
        arg1 as T
      <-
        rtn as T: T(arg0 + arg1) //Requires constructor T(-> arg0 as R2)

  defines function

    WithAdditionOperatorAndR3()
      //R3 is missing the R3(-> arg0 as R2) constructor required by constraint
      @Error: POST_RESOLUTION_CHECKS: CONSTRAINED_TYPE_CONSTRUCTOR_MISSING
      gf2 <- GF2() of R3
      assert gf2?

Solution

#!ek9
defines module bad.generic.constraining.resolution3

  defines record

    R2 as open
      value as Integer?

      R2() as pure
        value :=? 1

      R2() as pure
        -> arg0 as Integer
        value :=? Integer(arg0)

      R2() as pure
        -> arg0 as R2
        value :=? Integer(arg0.value)

      operator + as pure
        -> arg0 as R2
        <- rtn as R2: R2(value + arg0.value)

      default operator ?

    R4 is R2
      anotherValue as Integer?

      R4() as pure
        value :=? 1

      R4() as pure
        -> arg0 as Integer
        super(arg0)

      R4() as pure
        -> arg0 as R2  //Now includes required constructor
        super(arg0)

  defines function

    GF2 of type T constrain by R2
      ->
        arg0 as T
        arg1 as T
      <-
        rtn as T: T(arg0 + arg1)

  defines function

    WithAdditionOperatorAndR4()
      gf2 <- GF2() of R4  //R4 has all required constructors
      assert gf2?

See Also


E06140: Method Ambiguous

Classification: METHOD_AMBIGUOUS

Description

Multiple overloaded methods match the call signature with equal cost, making it impossible for the compiler to determine which method to invoke. EK9 uses a cost-based method resolution algorithm where each type conversion/coercion has an associated cost. When two or more methods have matching costs within a tolerance of 0.001, the call is ambiguous. This can occur when combining superclass inheritance and trait implementation, where the type hierarchy distances result in equal matching costs.

Example (Error)

#!ek9
defines module fuzztest.ambiguity.superclass.trait.equal

  defines trait
    T
      getValue() as abstract
        <- rtn as Integer?

  defines class
    A as open
      default A as pure

    B extends A as open
      default B as pure

    C extends B with trait of T
      default C as pure

      override getValue()
        <- rtn <- 42

  defines class
    TestClass
      checkMethod()
        -> arg as A
        <- rtn <- 10

      checkMethod()
        -> arg as T
        <- rtn <- 20

      default TestClass as pure

  defines program
    TestAmbiguity()
      c <- C()
      test <- TestClass()

      //AMBIGUOUS: C→B→A (0.05+0.05=0.10) vs C→T (0.10)
      @Error: FULL_RESOLUTION: METHOD_AMBIGUOUS
      test.checkMethod(c)

Solution: Make Match Costs Distinct

#!ek9
defines module fuzztest.ambiguity.superclass.trait.equal

  defines trait
    T
      getValue() as abstract
        <- rtn as Integer?

  defines class
    A as open
      default A as pure

    B extends A as open
      default B as pure

    C extends B with trait of T
      default C as pure

      override getValue()
        <- rtn <- 42

  defines class
    TestClass
      checkMethod()
        -> arg as B  //Use B (0.05) instead of A (0.10)
        <- rtn <- 10

      checkMethod()
        -> arg as T  //Cost 0.10
        <- rtn <- 20

      default TestClass as pure

  defines program
    TestAmbiguity()
      c <- C()
      test <- TestClass()

      test.checkMethod(c)  //Unambiguous: C→B (0.05) < C→T (0.10)

See Also


E06150: Methods Conflict

Classification: METHODS_CONFLICT

Description

When a class implements multiple traits or extends a base class while implementing traits, method conflicts can occur if multiple sources provide concrete (non-abstract) implementations of the same method. EK9 requires explicit resolution by overriding the conflicting method to provide a clear, single implementation. This prevents the diamond problem and ensures unambiguous method resolution.

Example (Error)

#!ek9
defines module clashing.implementations

  defines trait

    T1
      clash()
        <- rtn <- true

    T2
      clash()
        <- rtn <- false

  defines class

    Base as open
      clash()
        <- rtn <- false

      default operator ?

    //ERROR: Two traits both provide concrete clash() implementations
    @Error: FULL_RESOLUTION: METHODS_CONFLICT
    BrokenClass1 with trait T1, T2
      default BrokenClass1()

      someMethod()
        <- rtn <- true
      default operator ?

    //ERROR: Base class and trait both provide concrete clash()
    @Error: FULL_RESOLUTION: METHODS_CONFLICT
    BrokenClass2 extends Base with trait T1
      default BrokenClass2()

      someMethod()
        <- rtn <- true
      default operator ?

Solution

#!ek9
defines module clashing.implementations

  defines trait

    T1
      clash()
        <- rtn <- true

    T2
      clash()
        <- rtn <- false

  defines class

    Base as open
      clash()
        <- rtn <- false

      default operator ?

    //Provide explicit override to resolve conflict
    WorkingClass1 with trait T1, T2
      default WorkingClass1()

      override clash()
        <- rtn <- true  //Explicit resolution

      default operator ?

    WorkingClass2 is Base with trait T1
      default WorkingClass2()

      override clash()
        <- rtn <- true  //Explicit resolution

      default operator ?

See Also


E06160: Not Immediate Trait

Classification: NOT_IMMEDIATE_TRAIT

Description

EK9 restricts direct trait method calls to only those traits explicitly and immediately declared on the current class or dynamic class. You cannot call methods from traits inherited through a trait hierarchy (super-traits) or from traits on a parent class. This restriction prevents tight coupling to implementation details of inherited trait hierarchies, making code easier to refactor. Only explicitly declared immediate traits are accessible via direct trait method calls.

Example (Error)

#!ek9
defines module bad.directtraitcalls

  defines trait

    T2
      simpleMessage() as pure
        <- rtn as String: "T2"

    T1 extends T2
      anotherMessage() as pure
        <- rtn as String: "T1"

  defines class

    //Class explicitly declares T1, which extends T2
    inValidClassViaSuperTrait with trait of T1

      someMethod()
        //T2 is NOT immediate (it's a super-trait of T1)
        @Error: FULL_RESOLUTION: NOT_IMMEDIATE_TRAIT
        var1 <- T2.simpleMessage()
        assert var1?

        //T1 IS immediate, so this is allowed
        var2 <- T1.anotherMessage()
        assert var2?

    InvalidT2AccessFromClass

      someMethod()
        //T2 is not declared on this class at all
        @Error: FULL_RESOLUTION: NOT_IMMEDIATE_TRAIT
        var <- T2.simpleMessage()
        assert var?

Solution

#!ek9
defines module bad.directtraitcalls

  defines trait

    T2
      simpleMessage() as pure
        <- rtn as String: "T2"

    T1 extends T2
      anotherMessage() as pure
        <- rtn as String: "T1"

  defines class

    //Explicitly declare T2 as immediate trait
    ValidClass1 with trait of T2

      someMethod()
        var <- T2.simpleMessage()  //Now allowed
        assert var?

    //Explicitly declare both T1 and T2 if you need both
    ValidClass2 with trait of T1, T2

      someMethod()
        var1 <- T1.anotherMessage()  //Allowed
        var2 <- T2.simpleMessage()   //Allowed
        assert var1? and var2?

See Also


E06170: Trait Access Not Supported

Classification: TRAIT_ACCESS_NOT_SUPPORTED

Description

Direct trait method calls (e.g., TraitName.methodName()) are only supported within classes or dynamic classes that explicitly declare that trait. Functions, dynamic functions created within functions or classes without the trait, and other contexts cannot access trait methods directly. Trait method access is a special syntax restricted to contexts where the trait is part of the type's explicit contract.

Example (Error)

#!ek9
defines module bad.directtraitcalls

  defines trait

    T2
      simpleMessage() as pure
        <- rtn as String: "T2"

  defines function

    //Trait method call from standalone function - not allowed
    SomeFunction()
      @Error: FULL_RESOLUTION: TRAIT_ACCESS_NOT_SUPPORTED
      var <- T2.simpleMessage()
      assert var?

    //Dynamic function trying to access trait - not allowed
    TestDynamicFunction()
      var <- String()

      @Error: FULL_RESOLUTION: TRAIT_ACCESS_NOT_SUPPORTED
      someFunction <- () is Acceptor of String as function ( t :=: T2.simpleMessage() )
      someFunction(var)

  defines class

    //Even in a class with the trait, dynamic functions can't access it
    InvalidT2AccessFromDynamicFunctionInClass with trait of T2

      NotAllowedFromDynamicClass()
        var <- String()
        @Error: FULL_RESOLUTION: TRAIT_ACCESS_NOT_SUPPORTED
        someFunction <- () is Acceptor of String as function ( t :=: T2.simpleMessage() )
        someFunction(var)

Solution

#!ek9
defines module bad.directtraitcalls

  defines trait

    T2
      simpleMessage() as pure
        <- rtn as String: "T2"

    LenValidator
      validateLength() abstract
        -> p as String
        <- r as Boolean?

  defines function

    //Dynamic class explicitly declares traits - trait access allowed
    TestValidDynamicClass()
      lenValidator <- () trait of LenValidator, T2 as class
        override validateLength()
          -> p as String
          <- r as Boolean: false
          if p?
            message <- `${T2.simpleMessage()}: ${p}`  //Allowed
            r := length message < 300

      result <- lenValidator.validateLength("Steve")
      assert result?

See Also


E06180: Not Accessible

Classification: NOT_ACCESSIBLE

Description

A method, field, constructor, or other member is not accessible from the current context due to visibility modifiers. EK9 enforces strict access control: private members are only accessible within the defining class, protected members are accessible within the defining class and its subclasses, and public members (default) are accessible everywhere. This error occurs when attempting to access private members from outside the class, protected members from unrelated classes, or private constructors from subclasses.

Example (Error)

#!ek9
defines module bad.classmethod.access3

  defines class
    C1 as open

      default private C1()

      C1()
        -> arg0 as String
        //Public constructor

      private privMethod()
        <- rtn <- true

      protected protMethod()
        <- rtn <- true

    C2 extends C1
      C2()
        //Cannot call private constructor from subclass
        @Error: FULL_RESOLUTION: NOT_ACCESSIBLE
        super()

      checkAccessToPrivateMethodInSuper()
        //Cannot access private method from subclass
        @Error: FULL_RESOLUTION: NOT_ACCESSIBLE
        someValueFromPrivateMethod <- privMethod()
        assert someValueFromPrivateMethod?

    UnrelatedToC1

      default UnrelatedToC1()

      testProtectedAccessViaC2()
        -> arg0 as C2
        //Cannot access protected method from unrelated class
        @Error: FULL_RESOLUTION: NOT_ACCESSIBLE
        result1A <- arg0.protMethod()
        assert result1A?

      testPrivateAccessViaC2()
        -> arg0 as C2
        //Cannot access private method from unrelated class
        @Error: FULL_RESOLUTION: NOT_ACCESSIBLE
        result1A <- arg0.privMethod()
        assert result1A?

Solution

#!ek9
defines module bad.classmethod.access3

  defines class
    C1 as open

      default C1()  //Make constructor public

      C1()
        -> arg0 as String

      //Make methods public for external access
      privMethod()
        <- rtn <- true

      protMethod()
        <- rtn <- true

    C2 extends C1
      C2()
        super()  //Now accessible

      checkAccessToPrivateMethodInSuper()
        someValueFromPrivateMethod <- privMethod()  //Now accessible
        assert someValueFromPrivateMethod?

    UnrelatedToC1

      default UnrelatedToC1()

      testMethodAccessViaC2()
        -> arg0 as C2
        result1A <- arg0.protMethod()  //Now accessible
        result1B <- arg0.privMethod()  //Now accessible
        assert result1A? and result1B?

See Also


E06190: Result Must Have Different Types

Classification: RESULT_MUST_HAVE_DIFFERENT_TYPES

Description

The Result type is designed to distinguish between success and failure scenarios. It requires two different types: one for the success value (OK) and another for the error value. Using the same type for both parameters defeats the entire purpose of Result, as there would be no way to distinguish between successful results and error conditions.

Example (Error)

#!ek9
 is not allowed.
-?>
defines module fuzztest.typeconstraint.result.integer

  defines function
    invalidResultInteger() as abstract
      @Error: SYMBOL_DEFINITION: RESULT_MUST_HAVE_DIFFERENT_TYPES
      <- rtn as Result of (Integer, Integer)?

//EOF

Solution

#!ek9
defines module fuzztest.typeconstraint.result.integer

  defines function
    validResultInteger() as abstract
      <- rtn as Result of (Integer, Exception)?  //Different types

See Also


E06200: Parenthesis Not Required

Classification: PARENTHESIS_NOT_REQUIRED

Description

Parentheses () should not be used in this context. This error occurs when parentheses appear where they don't belong, particularly in generic type declarations. In EK9, parentheses indicate construction (creating a new instance), while of indicates type parameterization. These are distinct concepts and should not be mixed.

Common cases include: List() of String (incorrect), GenericClass() of Type (wrong), or val as List() of String (invalid declaration syntax).

Example (Error)

#!ek9

defines module incorrect.generic.uses

  defines function

    @Resolved: SYMBOL_DEFINITION: FUNCTION: "invalidPhase1IncorrectParenthesis1"
    invalidPhase1IncorrectParenthesis1()
      //Failure1
      //Use of parenthesis on lhs
      @Error: SYMBOL_DEFINITION: PARENTHESIS_NOT_REQUIRED
      val1 as List() of String?

      //Failure2
      //Then missing parenthesis
      @Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
      val2 as List of String: List of String

      //Failure3
      //Again missing parenthesis
      @Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
      val3 <- List of String

      //Failure4 - should be List() of List of String
      @Error: SYMBOL_DEFINITION: PARENTHESIS_NOT_REQUIRED
      val4 <- List() of List() of String

//EOF

Solution

#!ek9
defines module incorrect.generic.uses

  defines function

    validGenericSyntax()
      //Correct: () for construction, 'of Type' for parameterization
      val1 as List of String: List()

      //Correct: Construct and parameterize together
      val2 <- List() of String

      //Correct: Nested generics - only outermost gets ()
      val3 <- List() of List of String

See Also


E06210: Parenthesis Required

Classification: PARENTHESIS_REQUIRED

Description

Parentheses () are required but were omitted. This error occurs when attempting to use a type name or construct an instance without the required parentheses. In EK9, parentheses indicate construction - the creation of a new instance. Without parentheses, the compiler sees only a type reference, not an instance creation.

Common cases include: constructing objects on the right-hand side (List of String should be List() of String), or when a statement is meant to create an instance but lacks the construction operator.

Example (Error)

#!ek9

defines module incorrect.generic.uses

  defines function

    @Resolved: SYMBOL_DEFINITION: FUNCTION: "invalidPhase1IncorrectParenthesis1"
    invalidPhase1IncorrectParenthesis1()

      //Failure2 - missing () on rhs
      @Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
      val2 as List of String: List of String

      //Failure3 - missing () when constructing
      @Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
      val3 <- List of String

      //Failure5 - not allowed - as does not create a new 'List' as missing ()
      @Error: SYMBOL_DEFINITION: PARENTHESIS_REQUIRED
      List of String

//EOF

Solution

#!ek9
defines module incorrect.generic.uses

  defines function

    validConstructionSyntax()
      //Correct: () indicates construction
      val2 as List of String: List()

      //Correct: Construct with ()
      val3 <- List() of String

      //Correct: Create instance (has side effects)
      List() of String

See Also


E06220: Values And Type Incompatible

Classification: VALUES_AND_TYPE_INCOMPATIBLE

Description

EK9 supports type inference when constructor arguments are provided, OR explicit type parameterization with of Type, but not both simultaneously. This error occurs when you attempt to mix explicit type parameters with constructor values, which creates ambiguity - the compiler cannot determine whether to infer the type from the values or use the explicit type parameters.

Use GenericThing(32) for type inference, OR GenericThing() of Integer for explicit typing, but never GenericThing(32) of Integer.

Example (Error)

#!ek9

defines module incorrect.generic.uses

  defines class
    GenericThing of type T
      item as T?

      default GenericThing()

      GenericThing()
        -> arg as T
        this.item = arg

  defines function

    @Resolved: SYMBOL_DEFINITION: FUNCTION: "invalidMixOfInferenceAndExplicit"
    invalidMixOfInferenceAndExplicit()

      validInferenceCombination1 <- GenericThing() of Integer

      validInferenceCombination2 <- GenericThing(32)

      //Failure 13 - Not allowed because if parameters are provided in construction then we infer type.
      @Error: SYMBOL_DEFINITION: VALUES_AND_TYPE_INCOMPATIBLE
      invalidInferenceCombination <- GenericThing(32) of Integer

//EOF

Solution 1: Use Type Inference

#!ek9
defines module incorrect.generic.uses

  defines function
    useTypeInference()
      //Type is inferred from constructor argument (32 → Integer)
      thing <- GenericThing(32)

Solution 2: Use Explicit Type

#!ek9
defines module incorrect.generic.uses

  defines function
    useExplicitType()
      //Type is explicitly declared
      thing <- GenericThing() of Integer

See Also


E06230: Captured Variable Must Be Named

Classification: CAPTURED_VARIABLE_MUST_BE_NAMED

Description

When capturing values in dynamic classes or functions, EK9 distinguishes between simple identifiers (variables) and expressions (calculations, function calls, literals). Simple identifiers can be captured directly: (varName). However, expressions MUST use named parameter syntax: (name: expression). This requirement ensures clarity and prevents confusion about what values are being captured.

Common violations include: (getValue()) (function call without name), (100) (literal without name), or (10 + 20) (arithmetic without name). All must be written as (value: getValue()), (threshold: 100), etc.

Example (Error)

#!ek9

defines module fuzz.dynamic.capture.expression.unnamed

  defines function

    getValue() as pure
      <- rtn as Integer: 42

    discriminator() as abstract
      -> s as String
      <- rtn as Boolean?

  defines program

    TestCaptureExpressionWithoutName()

      //INVALID - function call without name
      @Error: SYMBOL_DEFINITION: CAPTURED_VARIABLE_MUST_BE_NAMED
      fn1 <- (getValue()) is discriminator as function
        rtn: length s > min

      //INVALID - literal without name
      @Error: SYMBOL_DEFINITION: CAPTURED_VARIABLE_MUST_BE_NAMED
      fn2 <- (100) is discriminator as function
        rtn: length s < max

      //INVALID - arithmetic expression without name
      @Error: SYMBOL_DEFINITION: CAPTURED_VARIABLE_MUST_BE_NAMED
      fn3 <- (10 + 20) is discriminator as function
        rtn: length s > threshold

//EOF

Solution

#!ek9
defines module fuzz.dynamic.capture.expression.unnamed

  defines program
    TestCaptureExpressionWithName()

      //VALID - function call WITH name
      fn1 <- (min: getValue()) is discriminator as function
        rtn: length s > min

      //VALID - literal WITH name
      fn2 <- (max: 100) is discriminator as function
        rtn: length s < max

      //VALID - expression WITH name
      fn3 <- (threshold: 10 + 20) is discriminator as function
        rtn: length s > threshold

See Also


E06240: Either All Parameters Named Or None

Classification: EITHER_ALL_PARAMETERS_NAMED_OR_NONE

Description

EK9 enforces consistency in parameter naming - either all parameters must be named or none can be named. Mixing named and unnamed (positional) parameters is prohibited to maintain code clarity and prevent confusion about parameter order and intent.

This rule applies to dynamic class/function captures, constructor calls, and method/function invocations. Valid: (min: 10, max: 100) (all named) or (10, 100) (all positional). Invalid: (min: 10, 100) (mixing named and positional).

Example (Error)

#!ek9

defines module fuzz.dynamic.capture.mixed.naming

  defines trait
    Comparator
      compare()
        -> value as String
        <- result as Boolean?

  defines program

    TestMixedNaming()
      min <- 10
      max <- 100

      //INVALID - mixing named (min:) and unnamed (max) parameters
      @Error: SYMBOL_DEFINITION: EITHER_ALL_PARAMETERS_NAMED_OR_NONE
      comparator <- (min: 10, max) with trait of Comparator as class
        override compare()
          -> value as String
          <- result as Boolean: length value > min and length value < max

      assert comparator?

//EOF

Solution 1: All Named

#!ek9
defines module fuzz.dynamic.capture.mixed.naming

  defines program
    TestAllNamed()
      min <- 10
      max <- 100

      //VALID - all parameters named
      comparator <- (min: 10, max: 100) with trait of Comparator as class
        override compare()
          -> value as String
          <- result as Boolean: length value > min and length value < max

Solution 2: All Positional

#!ek9
defines module fuzz.dynamic.capture.mixed.naming

  defines program
    TestAllPositional()
      min <- 10
      max <- 100

      //VALID - all parameters positional (simple identifiers)
      comparator <- (min, max) with trait of Comparator as class
        override compare()
          -> value as String
          <- result as Boolean: length value > min and length value < max

See Also


E06250: Named Parameters Must Match Arguments

Classification: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS

Description

When using named parameters, the parameter names MUST exactly match the declared argument names of the function, method, or constructor being called. Named parameters provide self-documenting code and prevent argument order errors, but the compiler strictly enforces name matching to ensure correctness.

Common violations: using badName: value when parameter is arg0, or swapping names like arg1: stringValue, arg0: intValue when types don't match positions.

Example (Error)

#!ek9
defines module bad.named.arguments.examples

  defines class
    C1
      aMethod()
        ->
          arg0 as String
          arg1 as Float
        assert arg0? and arg1?

  defines function

    showInvalidChainedMethodNamedAccess1()
      c2 <- C2()

      c2.c1().aMethod(
        @Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
        badName1: "Steve",
        @Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
        alsoABadName: 1
        )

    showInvalidChainedMethodNamedAccess2()
      c2 <- C2()

      c2.c1().aMethod(
        @Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
        arg1: "Steve",
        @Error: FULL_RESOLUTION: NAMED_PARAMETERS_MUST_MATCH_ARGUMENTS
        arg0: 1
        )

//EOF

Solution

#!ek9
defines module bad.named.arguments.examples

  defines function
    showValidChainedMethodNamedAccess()
      c2 <- C2()

      //Correct: parameter names match exactly
      c2.c1().aMethod(arg0: "Steve", arg1: 1)

See Also


E06260: Parameter Mismatch

Classification: PARAMETER_MISMATCH

Description

The argument types provided don't match the expected parameter types defined in the function, method, or constructor signature. This is a type compatibility error detected during method/function call resolution.

Example (Error)

#!ek9

defines module bad.parameter.mismatch

  defines function
    acceptsString()
      -> param as String
      <- rtn as String: param

    testParameterMismatch()
      <- rtn as String: acceptsString(123)  //Passing Integer instead of String - should trigger PARAMETER_MISMATCH

//EOF

Solution

#!ek9
defines module bad.parameter.mismatch

  defines function
    testParameterMismatch()
      <- rtn as String: acceptsString("123")  //Correct: String parameter

See Also


E06270: Function Parameter Mismatch

Classification: FUNCTION_PARAMETER_MISMATCH

Description

Function delegate parameters don't match the expected signature. This error occurs when calling a function delegate variable with arguments that don't match the delegate's declared parameter types. Common scenario: attempting to call a no-parameter function delegate (() <- Integer) with arguments, or vice versa.

Example (Error)

#!ek9
defines module bad.detailed.resolution

  defines function

    SomeFunction() as abstract
      <- rtn as Integer?

  defines class

    C3A
      //Now its not just a variable but a delegate
      method2 as SomeFunction?

      C3A()
        -> arg0 as SomeFunction
        this.method2: arg0

      method1()
        <- rtn as Boolean?

        //Now it really is a function delegate, but we pass the wrong parameters (not also has the wrong return type - see next test)
        @Error: FULL_RESOLUTION: FUNCTION_PARAMETER_MISMATCH
        rtn: method2(21)

//EOF

Solution

#!ek9
defines module bad.detailed.resolution

  defines class
    C6A
      method2 as SomeFunction?

      C6A()
        -> arg0 as SomeFunction
        this.method2: arg0

      method1()
        <- rtn as Integer?

        //Finally this will be OK - no parameters, correct return type
        rtn: method2()

See Also


E06280: Too Many Arguments

Classification: TOO_MANY_ARGUMENTS

Description

More arguments were provided than the operator/method/function accepts. This is detected during operator definition validation - each EK9 operator has a specific parameter count requirement, and providing extra parameters violates the operator contract.

Example (Error)

#!ek9
defines module fuzztest.operators.test018

  defines class

    Number
      value <- 0

      // Should fail - + operator requires exactly 1 parameter, not 2
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TOO_MANY_ARGUMENTS
      operator + as pure
        ->
          a as Number
          b as Number
        <- result as Number: Number()

//EOF

Solution

#!ek9
defines module fuzztest.operators.test018

  defines class
    Number
      value <- 0

      // Correct - + operator requires exactly 1 parameter
      operator + as pure
        -> arg as Number
        <- result as Number: Number()

See Also


E06290: Too Few Arguments

Classification: TOO_FEW_ARGUMENTS

Description

Fewer arguments were provided than the operator/method/function requires. This is detected during operator definition validation - each EK9 operator has specific parameter count requirements, and omitting required parameters violates the operator contract.

Example (Error)

#!ek9
defines module fuzztest.operators.test019

  defines class

    Value
      amount <- 0

      //Need a pure default constructor for the addition return
      default Value() as pure

      // Should fail - + operator requires 1 parameter, given 0
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: TOO_FEW_ARGUMENTS
      operator + as pure
        <- result as Value: Value()

//EOF

Solution

#!ek9
defines module fuzztest.operators.test019

  defines class
    Value
      amount <- 0

      default Value() as pure

      // Correct - + operator requires exactly 1 parameter
      operator + as pure
        -> arg as Value
        <- result as Value: Value()

See Also


E06300: Require One Argument

Classification: REQUIRE_ONE_ARGUMENT

Description

This stream operation requires exactly one argument but received zero or multiple arguments. Stream pipeline operators like map require a single function parameter to transform each element in the stream.

Example (Error)

#!ek9

defines module bad.streams3

  defines class
    StringCollector
      joined <- String()

      operator |
        -> arg0 as String
        if arg0?
          if joined?
            joined += " " + arg0
          else
            joined: String(arg0)

      override operator ? as pure
        <- rtn as Boolean: joined?

  defines function

    IncompatibleFunction1()
      ->
        arg0 as Integer
        arg1 as Integer
      <-
        rtn as String: ""

    BrokenStreamCatMap4()
      collector <- StringCollector()
      @Error: FULL_RESOLUTION: REQUIRE_ONE_ARGUMENT
      cat [1, 2, 3] | map with IncompatibleFunction1 > collector

//EOF

Solution

#!ek9
defines module bad.streams3

  defines function
    IntegerToString()
      -> value as Integer
      <- rtn as String: value? <- $value else String()

    SimpleStreamCatMap1()
      collector <- StringCollector()
      //Check if Integer can be mapped to a String and then output.
      cat [1, 2, 3] | map with IntegerToString > collector
      assert collector?

See Also


E06310: Require No Arguments

Classification: REQUIRE_NO_ARGUMENTS

Description

The CALL and ASYNC stream operators require functions with signature () -> T (no parameters, must return value). This error occurs when attempting to use these operators with functions that require parameters. The CALL/ASYNC operators invoke each function in the stream without arguments, making parameterized functions incompatible.

Example (Error)

#!ek9

defines module fuzztest.stream.call.async

  defines function

    
    ValidCallable()
      <- rtn as Integer: 42

    
    NeedsParam()
      -> n as Integer
      <- rtn as Integer: n * 2

    CallWithParameterizedFunction()
      
      @Error: FULL_RESOLUTION: REQUIRE_NO_ARGUMENTS
      result <- cat [NeedsParam] | call | collect as List of Integer
      assert result?

//EOF

Solution

#!ek9
defines module fuzztest.stream.call.async

  defines function
    ValidCallable()
      <- rtn as Integer: 42

    CorrectCallUsage()
      //Correct: ValidCallable takes no parameters and returns Integer
      result <- cat [ValidCallable] | call | collect as List of Integer
      assert result?

See Also


E06320: Invalid Number Of Parameters

Classification: INVALID_NUMBER_OF_PARAMETERS

Description

Dispatcher methods require 1 or 2 parameters, not zero or more than two. Dispatchers in EK9 use dynamic dispatch based on parameter types at runtime. A zero-parameter dispatcher cannot dispatch (no parameters to inspect), and more than two parameters would complicate the dispatch logic beyond the supported patterns.

Example (Error)

#!ek9
defines module fuzztest.dispatcher.purity.variant4

  // Variation 4: Dispatcher With Zero Parameters
  // Question: Can a dispatcher method have zero parameters?
  // Result: CONFIRMED - dispatchers require 1 or 2 parameters

  defines class
    ZeroParamDispatcher
      @Error: FULL_RESOLUTION: INVALID_NUMBER_OF_PARAMETERS
      process() as dispatcher
        assert true

      process()
        -> arg as Integer
        assert arg?

//EOF

Solution

#!ek9
defines module fuzztest.dispatcher.purity.variant4

  defines class
    ValidDispatcher
      //Correct: dispatcher with 1 parameter
      process() as dispatcher
        -> arg as Integer
        assert arg?

      process()
        -> arg as String
        assert arg?

See Also


E06330: Incompatible Type Arguments

Classification: INCOMPATIBLE_TYPE_ARGUMENTS

Description

Function parameter types don't match the stream pipeline's data type. This error occurs in stream processing when providing a function to operators like sort that expect specific argument types matching the stream's element type. For example, sorting a stream of R1 requires a comparator function with signature (R1, R1) -> Integer.

Example (Error)

#!ek9

defines module bad.streams6

  defines function

    InvalidProp1ComparatorC()
      ->
        o1 as Date
        o2 as R1
      <-
        rtn as Integer: 66

    InvalidProp1ComparatorE()
      ->
        o1 as Duration
        o2 as Duration
      <-
        rtn as Integer: 1

  defines record
    R1 as open
      prop1 as String: String()
      prop2 as Date: Date()

  defines function

    InvalidComparatorFunctionStreamCatSort3()
      collector <- StringCollector()

      @Error: FULL_RESOLUTION: INCOMPATIBLE_TYPE_ARGUMENTS
      cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with InvalidProp1ComparatorC > collector
      assert collector?

    InvalidComparatorFunctionStreamCatSort5()
      collector <- StringCollector()

      @Error: FULL_RESOLUTION: INCOMPATIBLE_TYPE_ARGUMENTS
      cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with InvalidProp1ComparatorE > collector
      assert collector?

//EOF

Solution

#!ek9
defines module bad.streams6

  defines function
    Prop1R1Comparator()
      ->
        o1 as R1
        o2 as R1
      <-
        rtn as Integer: o1.prop1 <=> o2.prop1

    ComparatorFunctionStreamCatSort4()
      collector <- StringCollector()

      //We would expect this to reorder the values and output - this would use the 'Integer <- Prop1R1Comparator(o1, o2)' function.
      cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with Prop1R1Comparator > collector
      assert collector?

See Also


Phase 07: FULL_RESOLUTION

Full resolution is the largest compilation phase, containing 92 errors (43% of all compiler errors). This phase performs complete type resolution, method resolution, operator validation, service definition checking, and control flow analysis. Errors are organized by category for easier navigation.

Control Flow Errors

E07340: Pre Flow Symbol Not Resolved

Classification: PRE_FLOW_SYMBOL_NOT_RESOLVED

Description

The compiler cannot determine the subject of flow control when using complex expressions involving object access and operators. In expressions like counter.value += 5, the flow subject is ambiguous - is it counter or value? This error occurs when a switch or if statement uses such expressions without explicit clarity about what is being tested.

EK9 requires unambiguous flow subjects. Use either simple identifiers or assign the result to a variable first, then use that variable as the flow subject.

Example (Error)

#!ek9

defines module fuzztest.preflow.switch.addassign

  defines record
    Counter
      value <- 0

  defines function
    switchWithAddAssign()
      counter <- Counter()
      result <- 0

      //SWITCH with objectAccessExpression += (no explicit control)
      //Error: Cannot determine flow subject - is it 'counter' or 'value'?
      @Error: FULL_RESOLUTION: PRE_FLOW_SYMBOL_NOT_RESOLVED
      switch counter.value += 5
        case > 3
          result := 10
        default
          result := 20

      assert result?

//EOF

Solution

#!ek9
defines module fuzztest.preflow.switch.addassign

  defines function
    switchWithAddAssignCorrected()
      counter <- Counter()
      result <- 0

      //Solution: Assign to variable first, then switch on it
      newValue <- counter.value + 5
      counter.value := newValue

      switch newValue
        case > 3
          result := 10
        default
          result := 20

      assert result?

See Also


E07350: Guard Used In Expression

Classification: GUARD_USED_IN_EXPRESSION

Description

Guards (?= or <- with conditional semantics) cannot be used in expression contexts because they may leave variables uninitialized. When a guard's condition fails (e.g., the value is unset), the guarded block doesn't execute, meaning any variables declared within or dependent on the guard remain uninitialized.

EK9's strict initialization checking detects this at **symbol definition time** (Phase 01). Guards are only valid in control flow statement positions (if/switch/while/for headers), never in expression contexts like assignments, returns, or switch expression subjects.

Example (Error)

#!ek9

defines module bad.guards

  defines function

    currentTemperature() as pure
      -> country as String
      <- temp as Integer?

      if country == "GB"
        temp :=? 20
      else if country == "DE"
        temp :=? 41
      else
        temp :=? 31

  defines program

    ASwitchWithGuard()
      temperature <- Integer()

      //The guard may leave 'resultText' un-initialized if the return from currentTemperature was 'un-set'
      //This would cause the switch block not to execute and hence the return of 'result' would not execute
      //We'd then have resultText looking like it has been initialised when in fact it has not been.
      //While technically you could accept this, on a visual scan it might be hard to work out what is wrong.
      //Hence EK9 does not allow guards in expressions like this
      @Error: SYMBOL_DEFINITION: GUARD_USED_IN_EXPRESSION
      resultText <- switch temperature ?= currentTemperature("GB") with temperature
        <- result String: String()
        case < 12
          result: "Moderate"

      assert resultText?

//EOF

Solution

#!ek9
defines module bad.guards

  defines program
    ASwitchWithGuardCorrected()
      temperature <- Integer()

      //Solution: Guard in control flow position (if statement)
      if temperature ?= currentTemperature("GB")
        resultText <- switch temperature
          <- result String: String()
          case < 12
            result: "Moderate"
          default
            result: "Unknown"

        assert resultText?

See Also


E07370: Statement Unreachable

Classification: STATEMENT_UNREACHABLE

Description

The compiler detects that all execution paths within a control flow construct will unconditionally throw an exception, making any implicit continuation unreachable. Even when there are no explicit statements following the exception, the implicit flow continuation (such as a loop's next iteration) becomes illogical. This error is caught early during symbol definition to prevent dead code that cannot execute.

Example (Error)

#!ek9
defines module bad.flowcontrol.examples

  defines function
    //Failure 1
    invalidForLoopOverValues()
      //There is an implicit continuation here, even though there are no
      //following statements after the exception, this will fail at the first.
      //It therefore not logical or normal.
      @Error: SYMBOL_DEFINITION: STATEMENT_UNREACHABLE
      for item in ["Alpha", "Beta", "Charlie"]
        throw Exception("This will always fail")

Solution 1: Add Conditional Logic

#!ek9
defines module bad.flowcontrol.examples

  defines function
    validForLoopWithCondition()
      for item in ["Alpha", "Beta", "Charlie"]
        if item == "Beta"
          throw Exception("Only fails for Beta")
        Stdout().println(item)

Solution 2: Remove Loop if Always Failing

#!ek9
defines module bad.flowcontrol.examples

  defines function
    simplifiedFailure()
      //If the intent is to always throw, simplify
      throw Exception("Direct failure without loop")

See Also


E07380: Return Unreachable

Classification: RETURN_UNREACHABLE

Description

The function declares a return value but all execution paths unconditionally throw exceptions, making the return statement unreachable. The compiler detects this during early symbol definition analysis by examining control flow - when both branches of an if/else (or all branches of a switch) throw exceptions, no execution path can reach the return initialization. This represents a contradiction between the function signature (which promises a return) and the implementation (which always throws).

Example (Error)

#!ek9
defines module bad.flowcontrol.examples

  defines function
    //Failure 2
    invalidIfElseStatement3()
      @Error: SYMBOL_DEFINITION: RETURN_UNREACHABLE
      <- rtn as String: "OK"
      condition <- true
      if condition
        throw Exception("Check If")
      else
        throw Exception("Check Else")

Solution 1: Remove Return Declaration

#!ek9
defines module bad.flowcontrol.examples

  defines function
    validThrowFunction()
      //No return declaration - function just throws
      condition <- true
      if condition
        throw Exception("Check If")
      else
        throw Exception("Check Else")

Solution 2: Add Valid Return Path

#!ek9
defines module bad.flowcontrol.examples

  defines function
    validConditionalFunction()
      <- rtn as String: "OK"
      condition <- shouldThrow()
      if condition
        throw Exception("Conditional failure")
      //Return is now reachable when condition is false
      rtn

See Also


E07390: Pointless Expression

Classification: POINTLESS_EXPRESSION

Description

Using a constant Boolean literal (true/false) as a control flow condition is pointless because the outcome is known at compile time. The compiler detects this during symbol definition to prevent dead code patterns. If a condition is always true, the if-statement serves no purpose - the code should execute unconditionally. If always false, the code block is unreachable and should be removed. This error typically indicates leftover debugging code or a logic mistake where a variable comparison was intended instead of a literal.

Example (Error)

#!ek9
defines module bad.flowcontrol.examples

  defines program
    //Failure 5
    BadProgram1()
      @Error: SYMBOL_DEFINITION: POINTLESS_EXPRESSION
      if true
        x <- 91
        assert x?

Solution 1: Remove Pointless Condition

#!ek9
defines module bad.flowcontrol.examples

  defines program
    GoodProgram1()
      //Just execute unconditionally
      x <- 91
      assert x?

Solution 2: Use Actual Condition

#!ek9
defines module bad.flowcontrol.examples

  defines program
    GoodProgram2()
      condition <- checkSomething()
      if condition
        x <- 91
        assert x?

See Also


Switch Statement Errors

E07310: Not All Enumerated Values Present In Switch

Classification: NOT_ALL_ENUMERATED_VALUES_PRESENT_IN_SWITCH

Description

When using a switch on an enumeration type with explicit value checks (simple case statements without expressions), EK9 requires exhaustive coverage - all enumerated values must be present in case statements. This compiler enforcement provides a critical safety mechanism: when new values are added to an enumeration, all existing switch statements will fail compilation, forcing developers to explicitly handle the new cases. If exhaustive checking is not desired, add a default case or use if/else logic instead.

Example (Error)

#!ek9
defines module bad.switches.enums

  defines type
    LimitedEnum
      A,
      B,
      C,
      D

  defines program
    CheckForIncompleteEnumerationCasesAsExpression()
      val <- LimitedEnum.A

      @Error: FULL_RESOLUTION: NOT_ALL_ENUMERATED_VALUES_PRESENT_IN_SWITCH
      basicResult <- switch val
        <- rtn as String: "OK"
        case LimitedEnum.A
          rtn: "Just A"

      assert basicResult?

Solution 1: Add All Missing Cases

#!ek9
defines module bad.switches.enums

  defines program
    CheckForCompleteEnumerationCases()
      val <- LimitedEnum.A

      basicResult <- switch val
        <- rtn as String: "OK"
        case LimitedEnum.A
          rtn: "Just A"
        case LimitedEnum.B, LimitedEnum.C, LimitedEnum.D
          rtn: "Other values"

      assert basicResult?

Solution 2: Add Default Case

#!ek9
defines module bad.switches.enums

  defines program
    CheckWithDefault()
      val <- LimitedEnum.A

      basicResult <- switch val
        <- rtn as String: "OK"
        case LimitedEnum.A
          rtn: "Just A"
        default
          rtn: "All other cases"

      assert basicResult?

See Also


E07320: Default Required In Switch Statement

Classification: DEFAULT_REQUIRED_IN_SWITCH_STATEMENT

Description

A default case is required in switch statements to handle all possible execution paths. Even when switching on an enumeration with all values explicitly covered, a default is still required because the enumeration variable itself might be unset. EK9's tri-state object model means variables can be absent, unset, or set - the default case must handle the unset scenario. Without it, the compiler cannot guarantee the switch statement handles all execution paths safely.

Example (Error)

#!ek9
defines module bad.switches.enums

  defines type
    LimitedEnum
      A, B, C, D

  defines program
    MissingDefaultInSwitchStatement1()

      result as String?
      val <- LimitedEnum.A

      //You may think why is a default needed, when all enum values are catered for.
      //But don't forget val might not actually be set, so that is also a situation to deal with
      @Error: FULL_RESOLUTION: DEFAULT_REQUIRED_IN_SWITCH_STATEMENT
      switch val
        case LimitedEnum.A
          result: "Just A"
        case LimitedEnum.B
          result: "Just B"
        case LimitedEnum.C
          result: "Just C"
        case LimitedEnum.D
          result: "Just D"

      assert result?

Solution

#!ek9
defines module bad.switches.enums

  defines program
    ValidSwitchWithDefault()

      result as String?
      val <- LimitedEnum.A

      switch val
        case LimitedEnum.A
          result: "Just A"
        case LimitedEnum.B
          result: "Just B"
        case LimitedEnum.C
          result: "Just C"
        case LimitedEnum.D
          result: "Just D"
        default
          result: "Val is unset"

      assert result?

See Also


E07330: Default Required In Switch Expression

Classification: DEFAULT_REQUIRED_IN_SWITCH_EXPRESSION

Description

Switch expressions (switches that return values) require a default case when the return variable is declared unset (nullable). Even when using expression-based case conditions (like < LimitedEnum.C), the compiler cannot guarantee all cases are exhaustively covered without a default. The switch expression must be able to produce a return value for every possible input, including edge cases not matched by explicit case conditions. If the return variable were initialized with a default value, the default case could be omitted.

Example (Error)

#!ek9
defines module bad.switches.enums

  defines type
    LimitedEnum
      A, B, C, D

  defines program
    InvalidEnumerationWithExpressionUnsetReturn()
      val <- LimitedEnum.A

      @Error: FULL_RESOLUTION: DEFAULT_REQUIRED_IN_SWITCH_EXPRESSION
      basicResult <- switch val
        <- rtn as String?
        case < LimitedEnum.C
          rtn: "Just A or B"

      assert basicResult?

Solution

#!ek9
defines module bad.switches.enums

  defines program
    ValidEnumerationWithDefault()
      val <- LimitedEnum.A

      basicResult <- switch val
        <- rtn as String: "OK"
        case < LimitedEnum.C
          rtn: "Just A or B"
        default
          rtn: "Val is not set"

      assert basicResult?

See Also


E07360: Application Selection Invalid

Classification: APPLICATION_SELECTION_INVALID

Description

The 'with application of' syntax is exclusively for program/application-level dependency injection and is not valid on methods. This construct enables external configuration to select specific implementations (like database adapters, payment providers, etc.) at deployment time. Attempting to use 'with application of' on a class method makes no sense because method implementation is resolved at compile time through the class definition, not through external application configuration. Use dependency injection via constructors or parameters instead.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines class

    C1 extends C0
      @Error: SYMBOL_DEFINITION: APPLICATION_SELECTION_INVALID
      methodX with application of abc
        v1 <- "Developer some how expecting application injection here"
        assert v1?

Solution

#!ek9
defines module example

  demo()
    value <- 42
    result <- switch value
      case 1
        <- "One"
      case 2
        <- "Two"
      default
        <- "Other"

See Also


Returning Block Errors

E07400: Returning Missing

Classification: RETURNING_MISSING

Description

Operators and methods that implement logic (have a body) must declare a return type using the <- syntax when they produce values. The compiler detects this during explicit type symbol definition because operators/methods are validated for completeness at this phase. Even simple operators like ! (factorial or clear) require an explicit return type declaration - the implementation cannot infer what type should be returned without explicit guidance from the developer.

Example (Error)

#!ek9
defines module bad.classes.operators.examples2

  defines class

    C3
      //So you can have factorial or clear but it must return some type
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: RETURNING_MISSING
      operator ! as pure
        val <- 1
        assert val?

Solution

#!ek9
defines module bad.classes.operators.examples2

  defines class

    C3
      //Factorial operator must declare return type
      operator ! as pure
        <- rtn as Integer: 1
        rtn

See Also


E07405: Returning Required

Classification: RETURNING_REQUIRED

Description

Switch expressions that assign to a left-hand side variable must declare a returning block using the <- syntax to specify what value the expression produces. This is a common mistake for developers new to EK9 - using case assignments (someResult: "value") without declaring the switch's return type. The compiler detects during full resolution that the switch expression is trying to return a value (because it's assigned to a variable) but lacks the required returning declaration.

Example (Error)

#!ek9
defines module bad.switches.use

  defines program
    InvalidProgramReturnRequired()
      val <- "ToCheck"

      //Here there is no return value but one is expected
      //This is likely to be how someone new to ek9 will get it wrong.
      @Error: FULL_RESOLUTION: RETURNING_REQUIRED
      someResult <- switch val
        case "ToCheck"
          someResult: "Jackpot"
        case "This"
          someResult: "Was this"
        case "This"
          someResult: "Was that"
        default
          someResult: "Nothing"

      //This will be a Void, but that now does have ? 'isSet' and is always false.
      assert someResult?

Solution

#!ek9
defines module bad.switches.use

  defines program
    ValidProgramWithReturning()
      val <- "ToCheck"

      //Declare the return type properly
      someResult <- switch val
        <- resultText as String?
        case "ToCheck"
          resultText: "Jackpot"
        case "This"
          resultText: "Was this"
        default
          resultText: "Nothing"

      assert someResult?

See Also


E07406: Returning Not Required

Classification: RETURNING_NOT_REQUIRED

Description

Switch statements (not expressions) used for control flow should not declare returning blocks because they don't assign to a left-hand side variable - they execute for side effects only. This is another common mistake for EK9 newcomers: adding a returning declaration (<- rtn as String) to a switch statement when the switch is not being assigned to a variable. The compiler detects during full resolution that the switch has a return declaration but no corresponding assignment target.

Example (Error)

#!ek9
defines module bad.switches.use

  defines program
    InvalidProgramReturnNotRequired()
      val <- "ToCheck"

      someResult <- String()

      //This is also how someone will get the returning part wrong on a switch.
      //In this case there is no lhs variable to return anything into.
      switch val
        @Error: FULL_RESOLUTION: RETURNING_NOT_REQUIRED
        <- rtn as String: String()
        case "ToCheck"
          someResult: "Jackpot"
        case "This"
          someResult: "Was this"
        case "This"
          someResult: "Was that"
        default
          someResult: "Nothing"

      assert someResult?

Solution: Remove Returning Declaration

#!ek9
defines module bad.switches.use

  defines program
    ValidProgramStatement()
      val <- "ToCheck"

      someResult <- String()

      //Just use switch statement without returning
      switch val
        case "ToCheck"
          someResult: "Jackpot"
        case "This"
          someResult: "Was this"
        default
          someResult: "Nothing"

      assert someResult?

See Also


E07410: Must Return Same As Construct Type

Classification: MUST_RETURN_SAME_AS_CONSTRUCT_TYPE

Description

Certain operators have strict return type requirements enforced by the EK9 language specification. The negate operator (~), increment (++), and decrement (--) operators must return the same type as the class they're defined in. This constraint exists because these operators represent transformations of the object itself (negation, increment, decrement) - they fundamentally return a modified version of the same type. The compiler validates this during explicit type symbol definition to ensure operator semantics match their mathematical/logical intent.

Example (Error)

#!ek9
defines module bad.classes.operators.examples2

  defines class

    C2
      //The not/negate operator
      //As such it accepts no parameters, but returns the same type as itself
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_SAME_AS_CONSTRUCT_TYPE
      operator ~ as pure
        <- rtn as Float: 1.0

Solution

#!ek9
defines module bad.classes.operators.examples2

  defines class

    C2
      //Negate operator must return C2 type
      operator ~ as pure
        <- rtn as C2: C2()

See Also


E07420: Must Not Return Same Type

Classification: MUST_NOT_RETURN_SAME_TYPE

Description

The promote operator (#^) has a strict requirement that it must return a DIFFERENT type than the class it's defined in. Promotion fundamentally means transforming a value to a higher-level or more general type - returning the same type defeats the purpose of promotion. This is the opposite constraint from operators like ~, ++, -- which must return the same type. The compiler enforces this during explicit type symbol definition to ensure promotion semantics are maintained.

Example (Error)

#!ek9
defines module bad.classes.operators.examples5

  defines class

    C5

      //This is not a promotion
      operator #^ as pure
        @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_NOT_RETURN_SAME_TYPE
        <- rtn as C5: this

Solution

#!ek9
defines module bad.classes.operators.examples5

  defines class

    C5

      //Promotion must return different type
      operator #^ as pure
        <- rtn as String: "Promoted to String"

See Also


E07430: Return Value Not Supported

Classification: RETURN_VALUE_NOT_SUPPORTED

Description

Mutation operators like copy (:=:), replace (:^:), and merge (:~:) fundamentally operate through side effects - they modify the object in place rather than producing new values. Therefore, return values are not supported for these operators. The compiler enforces this during explicit type symbol definition because allowing returns would contradict the mutation semantics these operators represent. Unlike mathematical operators that transform and return, mutation operators alter state directly.

Example (Error)

#!ek9
defines module bad.classes.operators.examples3

  defines class

    //Some of mutation operators. These typically accept something in, but never return anything.
    C3
      //Copy from arg0 into this, there is no return here
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: RETURN_VALUE_NOT_SUPPORTED
      operator :=:
        -> arg0 as Float
        <- rtn as Float: arg0
        assert arg0?

Solution

#!ek9
defines module bad.classes.operators.examples3

  defines class

    C3
      //Copy operator mutates in place, no return
      operator :=:
        -> arg0 as Float
        //Mutate this object with arg0's value
        assert arg0?

See Also


E07440: Covariance Mismatch

Classification: COVARIANCE_MISMATCH

Description

When overriding methods in a subclass, the return type must be covariant - either exactly the same type as the base method or a subtype (more specific type). This error occurs when an override attempts to change the return type to an incompatible type that is neither the same nor a subtype. The compiler enforces covariance during full resolution to ensure type safety: code expecting the base class return type must safely handle what the override returns. Note that coercion (like Integer→Float) does NOT satisfy covariance - the type hierarchy relationship must be structural.

Example (Error)

#!ek9
defines module bad.class.covariance

  defines class

    C1 as open
      someMethod1()
        -> arg0 as String
        <- rtn as Float: 1.0

    
    C2 extends C1
      @Error: FULL_RESOLUTION: COVARIANCE_MISMATCH
      override someMethod1()
        -> arg0 as String
        <- rtn as String: "Test"

Solution: Match Base Return Type

#!ek9
defines module bad.class.covariance

  defines class

    C1 as open
      someMethod1()
        -> arg0 as String
        <- rtn as Float: 1.0

    C2 extends C1
      override someMethod1()
        -> arg0 as String
        <- rtn as Float: 2.0  //Same return type as base

See Also


Operator Errors

Operator errors relate to operator definitions, usage restrictions, and purity requirements. EK9 has specific rules about which operators must be pure, operator naming conventions, and limitations on operator usage for certain types.

E07500: Operator Must Be Pure

Classification: OPERATOR_MUST_BE_PURE

Description

Query and comparison operators must be declared as pure in EK9 because they represent read-only operations that should not produce side effects. The contains, matches, ==, <>, <, >, <=, >=, <=>, ?, and #? operators all fall into this category. The compiler enforces this during explicit type symbol definition to ensure these fundamental operations remain side-effect-free, enabling optimizations and maintaining referential transparency.

Example (Error)

#!ek9
defines module bad.classes.operators.examples5

  defines class

    C5

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: OPERATOR_MUST_BE_PURE
      operator contains
        -> arg0 as Float
        <- rtn as Boolean: true

Solution

#!ek9
defines module bad.classes.operators.examples5

  defines class

    C5

      operator contains as pure
        -> arg0 as Float
        <- rtn as Boolean: true

See Also


E07510: Operator Cannot Be Pure

Classification: OPERATOR_CANNOT_BE_PURE

Description

Mutation operators fundamentally modify object state through side effects and therefore cannot be declared as pure. This includes copy (:=:), replace (:^:), merge (:~:), increment (++), decrement (--), and compound assignment operators (+=, -=, etc.). The compiler enforces this during explicit type symbol definition because allowing pure mutation operators would violate EK9's purity guarantees - pure functions must not produce observable side effects. Mutation is the opposite of purity.

Example (Error)

#!ek9
defines module bad.classes.operators.examples3

  defines class

    //Some of mutation operators. These typically accept something in, but never return anything.
    C3
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: OPERATOR_CANNOT_BE_PURE
      operator :=: as pure
        -> arg0 as C3
        assert arg0?

Solution

#!ek9
defines module bad.classes.operators.examples3

  defines class

    C3
      operator :=:  //Remove 'as pure' - mutation operators are inherently impure
        -> arg0 as C3
        assert arg0?

See Also


E07620: Operator Not Defined

Classification: OPERATOR_NOT_DEFINED

Description

An operator required for the expression is not defined on the type being used. This commonly occurs when using custom types in range expressions (in operator) or collection membership tests, which require comparison operators (<=>, ==) to be defined. EK9 does not provide default implementations of these operators on custom types - you must explicitly define them to enable these operations.

Example (Error)

#!ek9
defines module bad.range.collections

  defines class
    C1
      default C1()
      //But miss out any operators.

  defines function

    CheckBadSimpleIntegerRange5()
      var <- 16

      @Error: FULL_RESOLUTION: OPERATOR_NOT_DEFINED
      result <- var in C1() ... C1()

      @Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
      assert result

Solution 1: Define Required Operators

#!ek9
defines module bad.range.collections

  defines class
    C1
      value <- 0

      default C1()

      C1()
        -> arg as Integer
        value :=: arg

      operator <=> as pure
        -> arg as C1
        <- result as Integer: value <=> arg.value

      operator == as pure
        -> arg as C1
        <- result as Boolean: value == arg.value

  defines function

    CheckSimpleRange()
      var <- 16
      result <- var in C1(10) ... C1(30)  //Now valid
      assert result

Solution 2: Use Built-in Types

#!ek9
defines module bad.range.collections

  defines function

    CheckSimpleIntegerRange()
      var <- 16
      result <- var in 10 ... 30  //Built-in Integer has operators
      assert result

See Also


E07630: Operator Cannot Be Used On Enumeration

Classification: OPERATOR_CANNOT_BE_USED_ON_ENUMERATION

Description

An operator is being applied to an enumeration type definition itself, not to enumeration values. Operators like $ (String conversion), $$ (JSON conversion), and comparison operators can only be used on enumeration instances (values), not on the enumeration type. The enumeration type represents the complete set of possible values and cannot be converted or compared directly.

Example (Error)

#!ek9
defines module bad.enumeration.use

  defines type
    CardSuit
      Hearts
      Diamonds
      Clubs
      Spades

  defines function

    InvalidUseOfStringConversion()

      @Error: FULL_RESOLUTION: OPERATOR_CANNOT_BE_USED_ON_ENUMERATION
      suitAsString <- $CardSuit

      @Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
      assert suitAsString?

    InvalidUseOfComparisonOperators()

      @Error: FULL_RESOLUTION: OPERATOR_CANNOT_BE_USED_ON_ENUMERATION
      result <- CardSuit > CardSuit.Hearts

Solution: Use Enumeration Values

#!ek9
defines module bad.enumeration.use

  defines type
    CardSuit
      Hearts
      Diamonds
      Clubs
      Spades

  defines function

    ValidUseOfEnumeration()
      stdout <- Stdout()

      cat CardSuit > stdout

      for val in CardSuit
        stdout.println(`Value is ${val}`)

      assert CardSuit.Hearts < CardSuit.Clubs
      hearts <- CardSuit.Hearts

      assert hearts < CardSuit.Spades

See Also


E07640: Bad Not Equal Operator

Classification: BAD_NOT_EQUAL_OPERATOR

Description

EK9 uses <> for the not-equal operator, not != as in C-family languages. This design choice provides visual distinctiveness and aligns with mathematical notation. Attempting to define or use != as an operator will be rejected during type definition validation. Use <> for inequality comparisons and implement the <> operator method (not !=) when defining custom types.

Example (Error)

#!ek9
defines module bad.classes.operators.examples1

  defines class

    C1

      //Should use <> for not equals
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: BAD_NOT_EQUAL_OPERATOR
      operator != as pure
        -> arg0 as C1
        <- rtn as Boolean: true

Solution

#!ek9
defines module bad.classes.operators.examples1

  defines class

    C1
      value <- 0

      default C1()

      C1()
        -> arg as Integer
        value :=: arg

      operator <> as pure
        -> arg0 as C1
        <- rtn as Boolean: value <> arg0.value

  defines function

    ValidComparison()
      obj1 <- C1(10)
      obj2 <- C1(20)

      if obj1 <> obj2
        stdout.println("Objects are not equal")

See Also


E07650: Bad Not Operator

Classification: BAD_NOT_OPERATOR

Description

EK9 uses ~ (tilde) for the logical NOT operator, not ! or the word not as in some other languages. This design choice provides consistency with bitwise negation and visual clarity. Attempting to define an operator not will be rejected during type definition validation. For boolean negation in expressions and operator method implementations, always use ~.

Example (Error)

#!ek9
defines module bad.classes.operators.examples1

  defines class

    C1

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: BAD_NOT_OPERATOR
      operator not as pure
        -> arg0 as C1
        <- rtn as Boolean: true

Solution

#!ek9
defines module bad.classes.operators.examples1

  defines class

    C1
      isValid <- true

      default C1()

      C1()
        -> arg as Boolean
        isValid :=: arg

      operator ~ as pure
        <- rtn as Boolean: ~isValid

  defines function

    ValidNegation()
      obj1 <- C1(true)
      obj2 <- ~obj1

      enabled <- true
      if ~enabled
        stdout.println("Disabled")

See Also


E07660: Operator Name Used As Method

Classification: OPERATOR_NAME_USED_AS_METHOD

Description

Certain names are reserved exclusively for operator definitions and cannot be used as regular method names. These reserved names include close, contains, matches, empty, and length. While these names can be used as parameter or variable names within methods, attempting to define a method with one of these names will be rejected during symbol definition. This restriction prevents ambiguity between method calls and operator usage.

Example (Error)

#!ek9
defines module bad.classes.examples

  defines class

    C3 as abstract

      //Try and declare a method with the name of an operator
      @Error: SYMBOL_DEFINITION: OPERATOR_NAME_USED_AS_METHOD
      close
        val <- 1
        assert val?

      @Error: SYMBOL_DEFINITION: OPERATOR_NAME_USED_AS_METHOD
      contains
        val <- 1
        assert val?

      @Error: SYMBOL_DEFINITION: OPERATOR_NAME_USED_AS_METHOD
      length
        val <- 1
        assert val?

Solution: Use Reserved Names as Parameters Only

#!ek9
defines module bad.classes.examples

  defines class

    C3 as abstract

      //Reserved names CAN be used as parameters
      closeMethod()
        -> close as String
        assert close?

      containsMethod()
        -> contains as String
        assert contains?

      lengthMethod()
        -> length as String
        assert length?

      //Use different method names
      closeConnection()
        val <- 1
        assert val?

      hasValue()
        val <- 1
        assert val?

      getLength()
        val <- 1
        assert val?

See Also


E07670: Service Operator Not Supported

Classification: SERVICE_OPERATOR_NOT_SUPPORTED

Description

Services cannot define custom operators as HTTP endpoint mappings. Service definitions support HTTP method mappings (GET, POST, PUT, DELETE, etc.) with URI paths, but attempting to define an operator (like <~>) as a service endpoint will be rejected. Services are designed for HTTP request/response handling, and operator semantics do not apply to web service endpoints. Use standard HTTP methods for service operations.

Example (Error)

#!ek9
defines module bad.services.use

  defines service
    Addresses :/addresses

      //An invalid operator
      @Error: SYMBOL_DEFINITION: SERVICE_OPERATOR_NOT_SUPPORTED
      operator <~> :/
        -> request as HTTPRequest :=: REQUEST
        <- response as HTTPResponse: (request) trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: getNotOK()

Solution: Use HTTP Method Mappings

#!ek9
defines module bad.services.use

  defines service
    Addresses :/addresses

      byId() as GET for :/{address-id}
        -> addressId as AddressId :=: PATH "address-id"
        <- response as HTTPResponse: (addressId) trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: getOK()

      listAll() as GET for :/
        <- response as HTTPResponse: () trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: getAllAddresses()

      create() as POST for :/
        -> request as HTTPRequest :=: REQUEST
        <- response as HTTPResponse: (request) trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: createAddress()

See Also


Dispatcher, Iteration, Exception, and Mutability Errors

These errors relate to EK9's dispatcher mechanism for dynamic method resolution, iteration support via pipe operators, exception handling restrictions, and mutability constraints.

E07810: Dispatch Only Supported In Classes

Classification: DISPATCH_ONLY_SUPPORTED_IN_CLASSES

Description

Dispatcher methods (marked with as dispatcher) are only supported in classes, not in traits, components, or other constructs. Dispatchers provide dynamic method resolution based on parameter types, which requires the concrete implementation context that only classes provide. Traits define interfaces and cannot have dispatcher entry points. Use classes when you need dispatcher functionality.

Example (Error)

#!ek9
defines module bad.traits.examples

  defines trait

    T1

      @Error: SYMBOL_DEFINITION: DISPATCH_ONLY_SUPPORTED_IN_CLASSES
      anotherMethod() as dispatcher
        -> arg0 as String
        assert arg0?

Solution: Use Class Instead

#!ek9
defines module bad.traits.examples

  defines class

    C1

      anotherMethod() as dispatcher
        -> arg0 as String
        assert arg0?

      anotherMethod()
        -> arg0 as Integer
        assert arg0?

      anotherMethod()
        -> arg0 as Float
        assert arg0?

See Also


E07820: Dispatchers Only Have One Method Entry Point Marked

Classification: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED

Description

Only one method in a class can be marked as a dispatcher entry point (using as dispatcher). When you have multiple overloaded methods with the same name, exactly ONE must be designated as the dispatcher entry point that receives all calls and dynamically dispatches to the appropriate specialized method based on parameter types. All other overloaded methods should NOT be marked as dispatcher—they are the handler methods that the dispatcher delegates to.

Example (Error)

#!ek9
defines module bad.dispatchermethods

  defines class

    BadDispatcher6

      @Error: FULL_RESOLUTION: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
      process() as dispatcher
        @Error: FULL_RESOLUTION: INCOMPATIBLE_PARAMETER_GENUS
        -> arg as Any
        assert arg?

      @Error: FULL_RESOLUTION: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
      process() as dispatcher
        -> arg as Integer
        assert arg?

      @Error: FULL_RESOLUTION: DISPATCHERS_ONLY_HAVE_ONE_METHOD_ENTRY_POINT_MARKED
      process() as dispatcher
        -> arg as Float
        assert arg?

Solution

#!ek9
defines module bad.dispatchermethods

  defines class

    ValidDispatcher1 as open

      private process() as dispatcher  //Single dispatcher entry point
        -> arg as Any
        assert arg?

      //Regular handler methods (not marked as dispatcher)
      protected process()
        -> arg as Float
        assert arg?

      process()
        -> arg as Integer
        assert arg?

See Also


E07830: Unable To Find Pipe For Type

Classification: UNABLE_TO_FIND_PIPE_FOR_TYPE

Description

The stream terminator (the target receiving piped data using >) does not have an operator | that accepts the type being piped to it. For a type to receive piped data, it must define an operator | method that accepts the incoming type. This error occurs when you try to pipe data of one type into a collector or sink that expects a different, incompatible type. Check that the target type's operator | accepts the type being streamed.

Example (Error)

#!ek9
defines module bad.streams2

  defines class

    DurationCollector
      total as Duration: Duration()

      operator |
        -> arg0 as Duration  //Only accepts Duration
        assert arg0?

  defines function

    BrokenStatementTerminator4()
      collector <- DurationCollector()
      @Error: FULL_RESOLUTION: UNABLE_TO_FIND_PIPE_FOR_TYPE
      cat [1, 2, 3] > collector  //Trying to pipe Integer into Duration collector

Solution 1: Use Compatible Types

#!ek9
defines module bad.streams2

  defines class

    DurationCollector
      total as Duration: Duration()

      operator |
        -> arg0 as Duration
        assert arg0?

  defines function

    SimpleStreamCat2()
      collector <- DurationCollector()
      cat [P2D, P1W, PT2H] > collector  //Pipe Duration into Duration collector
      assert collector?

Solution 2: Implement Operator For Required Type

#!ek9
defines module bad.streams2

  defines class

    DurationCollector
      total as Duration: Duration()

      operator |
        -> arg0 as Duration
        assert arg0?

      operator |
        -> arg0 as Integer  //Add support for Integer
        total += Duration("PT" + $arg0 + "S")

See Also


E07840: Missing Iterate Method

Classification: MISSING_ITERATE_METHOD

Description

The type being iterated does not provide the required methods for iteration. EK9 supports iteration via two patterns: (1) an iterator() method returning an Iterator<T>, or (2) duck-typed hasNext() returning Boolean and next() returning the element type. Primitive types like Integer, Float, and Boolean are not iterable. Custom classes must implement one of these patterns completely—partial implementations (missing methods or incorrect return types) are rejected.

Example (Error)

#!ek9
defines module bad.forloops.check

  defines class

    C1
      hasNext()
        <- rtn <- false
      //Missing next() method

  defines function

    InvalidForLoop1()
      @Error: FULL_RESOLUTION: MISSING_ITERATE_METHOD
      for value in 34  //Integer is not iterable
        @Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
        assert value?

    InvalidForLoop2()
      @Error: FULL_RESOLUTION: MISSING_ITERATE_METHOD
      for value in C1()  //C1 has hasNext() but missing next()
        @Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
        assert value?

Solution: Implement Complete Iterator Protocol

#!ek9
defines module bad.forloops.check

  defines class

    C3
      hasNext()
        <- rtn <- false
      next()
        <- rtn as String: String()

  defines function

    ForLoopStatement3()
      for value in C3()  //Complete duck-typed iterator
        assert value?

See Also


E07850: Single Exception Only

Classification: SINGLE_EXCEPTION_ONLY

Description

The catch block can only accept a single exception parameter, not multiple exception types. EK9's exception handling model is simplified compared to languages like Java that support multiple catch clauses. Each try-catch block handles exactly one exception type. If you need to catch multiple exception types, use nested try-catch blocks or catch a common base exception type (like Exception) and handle different cases within the catch block.

Example (Error)

#!ek9
defines module bad.trycatchfinally.example

  defines function

    TryCatchMultipleExceptions()
      someValue <- 0

      try
        value <- exceptionFunction()
        assert value?
        someValue: value
      catch
        @Error: FULL_RESOLUTION: SINGLE_EXCEPTION_ONLY
        ->
          ex1 as BespokeException
          ex2 as Exception
        assert ex1? and ex2?
        someValue: -1

      assert someValue?

Solution 1: Catch Single Base Exception

#!ek9
defines module bad.trycatchfinally.example

  defines function

    TryCatchSingleException()
      someValue <- 0

      try
        value <- exceptionFunction()
        assert value?
        someValue: value
      catch
        -> ex as Exception  //Single exception parameter
        assert ex?
        someValue: -1

      assert someValue?

Solution 2: Nested Try-Catch for Different Types

#!ek9
defines module bad.trycatchfinally.example

  defines function

    NestedTryCatch()
      someValue <- 0

      try
        value <- bespokeExceptionFunction()
        assert value?
        someValue: value
      catch
        -> ex as BespokeException
        someValue: -1

      try
        value <- exceptionFunction()
        assert value?
        someValue: value
      catch
        -> ex as Exception
        someValue: -2

See Also


E07890: Not Mutable

Classification: NOT_MUTABLE

Description

An attempt is being made to mutate an immutable value. Constants (defined with defines constant) and enumeration values are inherently immutable and cannot be modified, assigned to, or used with mutation operators like ++, --, +=, or assignment operators like :=, :=:, :^:, :~:. Additionally, trying to mutate the result of a method call that returns nothing (void) will trigger this error. Also while loop variables can and do change, they are not mutable directly. So for example you cannot modify and mutate 'i' in a 'for i' style loop.

Example (Error)

#!ek9
defines module bad.mutations

  defines constant
    NAME <- "STEVE"
    NUMBERS <- 1
    DATE <- 2023-11-03

  defines function

    CheckNoMutationOfConstant()

      @Error: FULL_RESOLUTION: NOT_MUTABLE
      NUMBERS++

      @Error: FULL_RESOLUTION: NOT_MUTABLE
      DATE += P2D

    CheckNoAssignmentToConstant()

      @Error: FULL_RESOLUTION: NOT_MUTABLE
      NAME: "Stephen"

      @Error: FULL_RESOLUTION: NOT_MUTABLE
      NAME += "Snake"

Solution: Use Variables Instead of Constants

#!ek9
defines module bad.mutations

  defines constant
    INITIAL_NAME <- "STEVE"
    INITIAL_NUMBER <- 1
    INITIAL_DATE <- 2023-11-03

  defines function

    CheckMutationOfVariableIsOK()
      //mutation to a variable is fine.
      var <- 1
      var++
      var--
      var += 6
      assert var?

      date <- 2023-11-03
      date += P2D
      assert date?

See Also


E07900: Invalid Value

Classification: INVALID_VALUE

Description

A value provided does not meet the required format or constraints for its context. This commonly occurs with text construct language codes, which must follow the pattern [a-z]+(_[A-Z]+)? (lowercase language code, optional underscore followed by uppercase country code). Examples of valid codes: "en", "de", "en_GB", "fr_FR". Invalid formats include uppercase base codes, hyphens instead of underscores, or incorrect case for country codes.

Example (Error)

#!ek9
defines module bad.text.language.codes

  //Invalid: uppercase base (should be lowercase)
  @Error: SYMBOL_DEFINITION: INVALID_VALUE
  defines text for "EN"

    TestText1
      welcome()
        "Welcome"

  //Invalid: hyphen instead of underscore
  @Error: SYMBOL_DEFINITION: INVALID_VALUE
  defines text for "en-US"

    TestText3
      message()
        "Hi there"

  //Invalid: lowercase country code
  @Error: SYMBOL_DEFINITION: INVALID_VALUE
  defines text for "en_us"

    TestText4
      welcome()
        "Welcome"

Solution: Use Correct Language Code Format

#!ek9
defines module bad.text.language.codes

  defines text for "en"

    TestText1
      welcome()
        "Welcome"

  defines text for "en_US"

    TestText3
      message()
        "Hi there"

  defines text for "en_GB"

    TestText4
      welcome()
        "Welcome"

See Also


Web Service Errors

Web service errors relate to EK9's HTTP service definitions, including URI patterns, HTTP verbs, parameter handling, and return types. EK9 services provide built-in HTTP capabilities with strong type checking.

E07680: Service URI With Vars Not Supported

Classification: SERVICE_URI_WITH_VARS_NOT_SUPPORTED

Description

The service base URI (the root path defined in the service declaration) cannot contain path variables. While individual HTTP method endpoints can use path variables (like :/{id}), the service's root URI must be a fixed path. This ensures that the service has a stable base address for routing, with variable paths handled at the HTTP method level. Move path variables from the service base URI to the specific HTTP method mappings.

Example (Error)

#!ek9
defines module bad.services.use

  defines service
    //The Name of the and the uri it is mapped to - but break the code by using a parameter.
    @Error: SYMBOL_DEFINITION: SERVICE_URI_WITH_VARS_NOT_SUPPORTED
    Addresses :/addresses/{not-allowed}

      byId() as GET for :/{address-id}
        -> addressId as AddressId :=: PATH "address-id"
        <- response as HTTPResponse: (addressId) trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: getOK()

Solution: Use Fixed Service URI, Variables in Methods

#!ek9
defines module bad.services.use

  defines service
    Addresses :/addresses  //Fixed base URI

      byId() as GET for :/{address-id}  //Variables allowed here
        -> addressId as AddressId :=: PATH "address-id"
        <- response as HTTPResponse: (addressId) trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: getOK()

      listAll() as GET for :/  //No variables needed
        <- response as HTTPResponse: () trait HTTPResponse by delegate as class
          override content()
            <- rtn as String: getAllAddresses()

See Also


E07690: Service HTTP Access Not Supported

Classification: SERVICE_HTTP_ACCESS_NOT_SUPPORTED

Description

HTTP access annotations (:=: REQUEST, :=: CONTENT, :=: PATH, etc.) can only be used in HTTP-mapped methods (GET, POST, PUT, DELETE, etc.), not in regular service methods (private or public helper methods). These annotations provide direct access to HTTP request data and are meaningful only in the context of handling HTTP requests. Helper methods that don't map to HTTP endpoints cannot use these annotations because there is no HTTP request/response to access.

Example (Error)

#!ek9
defines module bad.services.use

  defines service
    Addresses :/addresses

      //Not allowed because this is just a normal method and 'correlation' to http is not possible.
      @Error: SYMBOL_DEFINITION: IMPLEMENTATION_MUST_BE_PROVIDED
      private someMethod()
        @Error: SYMBOL_DEFINITION: SERVICE_HTTP_ACCESS_NOT_SUPPORTED
        -> incomingContent as String :=: CONTENT

      @Error: SYMBOL_DEFINITION: IMPLEMENTATION_MUST_BE_PROVIDED
      private anotherInvalidMethod()
        @Error: SYMBOL_DEFINITION: SERVICE_HTTP_ACCESS_NOT_SUPPORTED
        <- incomingContent as String :=: CONTENT

Solution: Use HTTP Annotations Only in HTTP Methods

#!ek9
defines module bad.services.use

  defines service
    Addresses :/addresses

      //HTTP annotations allowed in HTTP-mapped methods
      create() as POST for :/
        -> incomingContent as String :=: CONTENT
        <- response as HTTPResponse: processContent(incomingContent)

      byId() as GET for :/{address-id}
        -> addressId as AddressId :=: PATH "address-id"
        <- response as HTTPResponse: getAddress(addressId)

      //Helper methods use normal parameters (no HTTP annotations)
      private processContent()
        -> content as String
        <- response as HTTPResponse: createResponse(content)

      private getAddress()
        -> id as AddressId
        <- response as HTTPResponse: lookupAddress(id)

See Also


E07700: Service HTTP Path Param Invalid

Classification: SERVICE_HTTP_PATH_PARAM_INVALID

Description

The HTTP path parameter name does not match any variable declared in the URI pattern. When using :=: PATH "parameter-name", the string literal must exactly match a path variable in the URI (defined with :/{variable-name}). When using implicit PATH (just the parameter name without explicit qualifier), the parameter name must match a URI path variable. Check for typos, case mismatches, or incorrect parameter names.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid5() :/{incorrect-name}/invalid5.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_PARAM_INVALID
          arg0 as Integer :=: PATH "no-such-path"  //Name doesn't match URI variable
        <-
          response as HTTPResponse: () with trait of HTTPResponse

      invalid6() :/{non-such}/invalid6.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_PARAM_INVALID
          nonSuch as Integer  //Assume PATH but 'nonSuch' != 'non-such'
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution: Match URI Path Variable Names

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index4() :/{address-id}
        -> addressId as Integer :=: PATH "address-id"  //Explicit match
        <- response as HTTPResponse: () with trait of HTTPResponse

      index5() :/{address-id}/{routeId}/someResource.xml
        ->
          addressId as Integer :=: PATH "address-id"
          routeId as String  //Implicit PATH, name matches URI variable
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07710: Service HTTP Param Needs Qualifier

Classification: SERVICE_HTTP_PARAM_NEEDS_QUALIFIER

Description

HTTP parameters accessed via HEADER, QUERY, or PATH require a string qualifier specifying the parameter name from the HTTP request. The syntax :=: HEADER, :=: QUERY, or :=: PATH without a following string literal is incomplete. You must provide the actual HTTP parameter name as a string, like :=: HEADER "Authorization" or :=: QUERY "page". Only REQUEST does not require a qualifier since it represents the entire request object.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid8() :/invalid8.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PARAM_NEEDS_QUALIFIER
          arg0 as Integer :=: HEADER  //Missing parameter name qualifier
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution: Add String Qualifier

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index2() :/{index-number}/index2.html
        ->
          queryId as Integer :=: PATH "index-number"
          fromDate as Date :=: QUERY "from"
          duration as Duration :=: QUERY "duration"
          timeOut as Millisecond :=: QUERY "time-out"
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07720: Service HTTP Param Qualifier Not Allowed

Classification: SERVICE_HTTP_PARAM_QUALIFIER_NOT_ALLOWED

Description

The REQUEST parameter type does not accept a string qualifier. While QUERY, HEADER, and PATH require string qualifiers to specify which HTTP parameter to access (e.g., :=: QUERY "page"), REQUEST represents the entire HTTP request object and should be used as :=: REQUEST without any additional string. Providing a string qualifier after REQUEST is invalid syntax.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid9() :/invalid9.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PARAM_QUALIFIER_NOT_ALLOWED
          arg0 as HTTPRequest :=: REQUEST "not-required"  //String qualifier not allowed
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution: Remove String Qualifier

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index3() :/index3.html
        ->
          request as HTTPRequest :=: REQUEST  //No qualifier needed
        <-
          response as HTTPResponse: () with trait of HTTPResponse

      //String qualifiers only for QUERY, HEADER, PATH
      index2() :/{index-number}/index2.html
        ->
          queryId as Integer :=: PATH "index-number"  //Qualifier required
          fromDate as Date :=: QUERY "from"  //Qualifier required
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07730: Service HTTP Path Param Count Invalid

Classification: SERVICE_HTTP_PATH_PARAM_COUNT_INVALID

Description

The number of path variables in the URI pattern does not match the number of PATH parameters declared in the method signature. Every path variable defined in the URI (using :/{variable-name}) must have a corresponding parameter declaration. If the URI contains two path variables, the method must declare exactly two PATH parameters. This ensures all URI components can be properly mapped to method parameters.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_HTTP_PATH_PARAM_COUNT_INVALID
      invalid7() :/{validationNumber}/{undefined}/invalid7.html
        -> validationNumber as Integer  //Missing parameter for {undefined}
        <- response as HTTPResponse: () with trait of HTTPResponse

Solution: Declare All Path Parameters

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index5() :/{address-id}/{routeId}/someResource.xml
        ->
          addressId as Integer :=: PATH "address-id"
          routeId as String  //Both path variables declared
        <-
          response as HTTPResponse: () with trait of HTTPResponse

      //Single variable - one parameter
      index4() :/{address-id}
        -> addressId as Integer :=: PATH "address-id"
        <- response as HTTPResponse: () with trait of HTTPResponse

See Also


E07740: Service With No Body Provided

Classification: SERVICE_WITH_NO_BODY_PROVIDED

Description

Service methods must have a complete implementation body. EK9 services do not support abstract or unimplemented methods. Every service operation (GET, POST, PUT, DELETE, etc.) must provide the implementation logic that returns an HTTPResponse. This is enforced during the explicit type symbol definition phase to ensure all web service endpoints are fully implemented before the service can be compiled and deployed.

Example (Error)

#!ek9
defines module bad.servicemethod.returntypes

  defines service

    S1 for :/site1/place1

      //Service method without implementation - only declaration
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_WITH_NO_BODY_PROVIDED
      anotherMethod() as GET for :/yetAnother/path
        <- response as HTTPResponse?

Solution: Provide Implementation

#!ek9
defines service
  MyService
    GET for "/api/data"
      <- result as HTTPResponse: HTTPResponse().ok("{\"status\": \"success\"}")

    POST for "/api/data"
      -> request as HTTPRequest
      <- result as HTTPResponse
        //Process request
        data <- request.body()
        result := HTTPResponse().created("{\"id\": 123}")

See Also


E07750: Service Incompatible Return Type

Classification: SERVICE_INCOMPATIBLE_RETURN_TYPE

Description

EK9 web service operations must return HTTPResponse. Service methods cannot return primitive types (Integer, String, Boolean, etc.) or domain objects directly. The HTTPResponse type is specifically designed to handle HTTP status codes, headers, and content formatting. This requirement ensures that all HTTP responses are properly structured with status codes, content types, and other HTTP-specific metadata. The compiler enforces this during explicit type symbol definition to catch return type mismatches early in compilation.

Example (Error)

#!ek9
defines module fuzztest.service.return.integer

  defines service
    IntegerReturnService :/numbers

      
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_RETURN_TYPE
      getCount() as GET :/count
        <- count as Integer: 42

Solution: Return HTTPResponse

#!ek9
defines service
  MyService
    GET for "/api/data"
      <- result as HTTPResponse: HTTPResponse().ok("Hello")

    GET for "/api/json"
      <- result as HTTPResponse
        data <- "{\"message\": \"Hello\"}"
        result := HTTPResponse().ok(data).contentType("application/json")

See Also


E07760: Service Incompatible Param Type

Classification: SERVICE_INCOMPATIBLE_PARAM_TYPE

Description

HTTP service parameters (QUERY, PATH, HEADER, CONTENT) support only a limited set of built-in types that can be reliably parsed from HTTP request data: Integer, String, Date, Time, DateTime, Millisecond, and Duration. Types like Float, Money, and custom classes are not supported because HTTP parameters are text-based and EK9 requires unambiguous, safe conversion. Use supported types or parse complex data from the request body.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid1() :/invalid1.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_PARAM_TYPE
          arg0 as Float :=: QUERY "some-param"  //Float not supported
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution: Use Supported Types

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index2() :/{index-number}/index2.html
        ->
          queryId as Integer :=: PATH "index-number"
          fromDate as Date :=: QUERY "from"
          duration as Duration :=: QUERY "duration"
          timeOut as Millisecond :=: QUERY "time-out"
          coverEnd as DateTime :=: QUERY "end-date-time"
          graceTime as Time :=: QUERY "grace-time"
          content as String :=: CONTENT
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07770: Service Incompatible Param Type Request

Classification: SERVICE_INCOMPATIBLE_PARAM_TYPE_REQUEST

Description

When using :=: REQUEST to access the full HTTP request, the parameter type must be HTTPRequest, not any other type like String, Integer, or custom types. The REQUEST qualifier specifically binds to the complete request object, so only HTTPRequest is compatible. If you need specific data from the request (like body content or headers), either use HTTPRequest and extract what you need, or use specific qualifiers like :=: CONTENT or :=: HEADER with appropriate types.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid2() :/invalid2.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_PARAM_TYPE_REQUEST
          arg0 as String :=: REQUEST  //Must be HTTPRequest, not String
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution: Use HTTPRequest Type

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index3() :/index3.html
        ->
          request as HTTPRequest :=: REQUEST  //Correct type
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07780: Service Request By Itself

Classification: SERVICE_REQUEST_BY_ITSELF

Description

When using HTTPRequest as a parameter (via :=: REQUEST), it must be the ONLY parameter in the method signature. You cannot combine HTTPRequest with other HTTP parameters like QUERY, PATH, HEADER, or CONTENT. This is because HTTPRequest already provides access to all request data (path parameters, query strings, headers, body), so mixing it with other parameter bindings would be redundant and ambiguous. Choose either HTTPRequest alone or individual parameter bindings.

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid4() :/invalid4.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_REQUEST_BY_ITSELF
          arg0 as HTTPRequest :=: REQUEST
          arg1 as Integer :=: QUERY "some-query"  //Cannot mix REQUEST with others
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution 1: Use HTTPRequest Alone

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index3() :/index3.html
        ->
          request as HTTPRequest :=: REQUEST  //Only parameter
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution 2: Use Individual Parameters

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      index2() :/{index-number}/index2.html
        ->
          queryId as Integer :=: PATH "index-number"
          fromDate as Date :=: QUERY "from"
          content as String :=: CONTENT
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07790: Service Incompatible Param Type Non Request

Classification: SERVICE_INCOMPATIBLE_PARAM_TYPE_NON_REQUEST

Description

The HTTPRequest type cannot be used with QUERY, PATH, HEADER, or CONTENT qualifiers. HTTPRequest is specifically designed to work with the :=: REQUEST qualifier to access the entire request object. If you want to access specific parts of the request (query parameters, path variables, headers, or body content), use the appropriate built-in types (String, Integer, Date, etc.) with the corresponding qualifiers, not HTTPRequest. This error occurs when attempting to declare a parameter like arg as HTTPRequest :=: QUERY "param".

Example (Error)

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      invalid3() :/invalid3.html
        ->
          @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_INCOMPATIBLE_PARAM_TYPE_NON_REQUEST
          arg0 as HTTPRequest :=: QUERY "another-parameter"  //HTTPRequest not compatible with QUERY
        <-
          response as HTTPResponse: () with trait of HTTPResponse

Solution: Use Appropriate Types for Each Qualifier

#!ek9
defines module bad.servicemethod.argumenttypes

  defines service

    S2 for :/site1/place2

      //Use HTTPRequest only with REQUEST
      index3() :/index3.html
        ->
          request as HTTPRequest :=: REQUEST
        <-
          response as HTTPResponse: () with trait of HTTPResponse

      //Use appropriate types for QUERY, PATH, etc.
      index2() :/{index-number}/index2.html
        ->
          queryId as Integer :=: PATH "index-number"
          fromDate as Date :=: QUERY "from"
          content as String :=: CONTENT
        <-
          response as HTTPResponse: () with trait of HTTPResponse

See Also


E07800: Service Missing Return

Classification: SERVICE_MISSING_RETURN

Description

Every EK9 web service method must explicitly declare a return value of type HTTPResponse. Service operations cannot be void - they must always produce an HTTP response to send back to the client. This is a fundamental requirement of web service implementations: every HTTP request must receive an HTTP response with appropriate status code, headers, and content. The compiler enforces this during explicit type symbol definition, ensuring that no service endpoint can be compiled without a declared HTTPResponse return value.

Example (Error)

#!ek9
defines module fuzztest.service.return.missing.method

  defines service
    MissingReturnMethodService :/resources

      
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: SERVICE_MISSING_RETURN
      getResource() as GET :/{id}
        -> id as String

        stdout <- Stdout()
        stdout.println(id)

Solution: Declare HTTPResponse Return

#!ek9
defines service
  MyService
    GET for "/api/data"
      <- result as HTTPResponse  //Declare return
        result := HTTPResponse().ok("{\"status\": \"ok\"}")

    POST for "/api/create"
      -> request as HTTPRequest
      <- result as HTTPResponse
        data <- request.body()
        result := HTTPResponse().created("{\"id\": 123}")

See Also


Function and Delegate Errors

Function and delegate errors relate to function parameter requirements, return type constraints, function delegates, and program argument handling. EK9's functional programming features require specific signatures for different use cases.

E07450: Function Must Have No Parameters

Classification: FUNCTION_MUST_HAVE_NO_PARAMETERS

Description

The function must have no parameters for this context. Stream operations like 'head', 'tail', and 'skip' can accept functions to dynamically determine how many elements to take/skip, but these functions must not accept any parameters - they simply return an Integer indicating the count. The function is called at stream execution time to get the dynamic value. If your function requires parameters, you cannot use it in this context - use a fixed integer value or a parameterless function instead.

Example (Error)

#!ek9
defines module bad.streams10

  defines function

    AcceptsArgument()
      -> arg0 as Integer
      <- rtn as Integer: arg0

    InvalidStreamCatHeadWithFunction2()
      collection <- List() of String
      @Error: FULL_RESOLUTION: FUNCTION_MUST_HAVE_NO_PARAMETERS
      cat ["last", "first"] | head AcceptsArgument > collection
      assert collection?

Solution

#!ek9
defines function
  generate()  //No parameters
    <- result as String: "Generated"

  //Use in supplier context
  supplier <- generate  //Function reference

See Also


E07460: Function Must Have Single Parameter

Classification: FUNCTION_MUST_HAVE_SINGLE_PARAMETER

Description

The function must have exactly one parameter for this stream operation context. Operations like 'uniq', 'map', 'filter', and 'select' require functions that accept a single parameter (the current stream element) and perform transformation, filtering, or uniqueness checking. The function signature must match: one input parameter and appropriate return type for the operation. Functions with zero parameters, two parameters, or more cannot be used in these single-parameter contexts.

Example (Error)

#!ek9
defines module bad.streams14

  defines function

    JustACouple()
      <- rtn <- 3

    StreamCatExpressionTailWithFunctionInvalidUniq()
      collection <- cat [4, 3, 2, 9, 3, 4, 1, 5, 5, 7]
        | filter with GreaterThanCheck
        @Error: FULL_RESOLUTION: FUNCTION_MUST_HAVE_SINGLE_PARAMETER
        | uniq JustACouple
        | sort
        | tail by JustACouple
        | collect as List of Integer

      assert collection?

Solution

#!ek9
defines function
  transform()
    -> value as Integer  //Single parameter
    <- result as String: String(value * 2)

defines function
  demo()
    numbers <- [1, 2, 3]
    result <- cat numbers | map with transform | collect as List of String

See Also


E07470: Function Must Have Two Parameters

Classification: FUNCTION_MUST_HAVE_TWO_PARAMETERS

Description

The function must have exactly two parameters for this stream operation context. Comparator functions used with 'sort' require two parameters of the same type to compare elements. Similarly, 'join' operations require functions accepting two parameters (one from each stream) to determine how to combine elements. The comparator signature must be: two input parameters of type T, returning Integer (-1, 0, or 1 for less-than, equal, greater-than). Functions with fewer or more than two parameters cannot satisfy this contract.

Example (Error)

#!ek9
defines module bad.streams6

  defines function

    InvalidProp1ComparatorD()
      ->
        o2 as R1
      <-
        rtn as Integer: 1

    InvalidComparatorFunctionStreamCatSort4()
      collector <- StringCollector()

      @Error: FULL_RESOLUTION: FUNCTION_MUST_HAVE_TWO_PARAMETERS
      cat [R1("last", 2010-10-01), R1("last", 2010-10-02), R1("first", 2010-10-01)] | sort with InvalidProp1ComparatorD > collector
      assert collector?

Solution

#!ek9
defines function
  combine()
    ->
      accumulator as Integer
      value as Integer  //Two parameters

defines function
  demo()
    numbers <- [1, 2, 3, 4, 5]
    sum <- cat numbers | reduce by combine

See Also


E07480: Not A Function Delegate

Classification: NOT_A_FUNCTION_DELEGATE

Description

The variable being called with function syntax is not a function delegate. Function delegates are references to executable code (functions, abstract functions) that can be invoked. Regular variables like integers, strings, or objects cannot be called with function invocation syntax `()` unless they are actual function delegates. This error occurs when attempting to invoke a non-callable value as if it were a function. Ensure the variable holds a function reference or use the correct access syntax for the type.

Example (Error)

#!ek9
defines module bad.functiondelegates.examples

  defines function

    SomeFunction()
      <- rtn <- true

    AnotherFunction()
      nonDelegate <- 1
      assert nonDelegate?

      //Check to make sure this can be detected, it's just an integer
      @Error: FULL_RESOLUTION: NOT_A_FUNCTION_DELEGATE
      notValidResult <- nonDelegate()
      @Error: FULL_RESOLUTION: TYPE_NOT_RESOLVED
      assert notValidResult?

Solution: Use Function Reference

#!ek9
defines function
  transformer()
    -> value as Integer
    <- result as String: String(value * 2)

defines function
  demo()
    result <- cat [1, 2, 3] | map with transformer | collect as List of String

See Also


E07490: Function Must Return Value

Classification: FUNCTION_MUST_RETURN_VALUE

Description

The function must return a value for use in this stream operation context. Stream operations like 'call' and 'async' require functions that produce return values which become the stream output. Functions without return values (void/procedure-like functions) cannot be used in these contexts because the stream has no data to pass to subsequent operations. The function must declare a return type using the `<- rtn as Type` syntax. If side effects are needed without producing stream values, consider using different stream termination operations.

Example (Error)

#!ek9
defines module bad.streams5

  defines function

    doesNotReturnAnything()
      value <- 1
      assert value?

    BrokenStreamCatCall3()
      collector <- StringCollector()

      @Error: FULL_RESOLUTION: FUNCTION_MUST_RETURN_VALUE
      cat [doesNotReturnAnything] | call > collector

      assert collector?

Solution

#!ek9
defines function
  process()
    -> value as Integer
    <- result as Boolean: true  //Return value declared
      stdout.println(value)

  //Or use in expression
  transform()
    -> value as Integer
    <- result as String: String(value * 2)

See Also


E07520: Must Return Boolean

Classification: MUST_RETURN_BOOLEAN

Description

The ? (isSet) operator must return a Boolean value because it represents a fundamental query: "is this object set/initialized with a meaningful value?" The compiler enforces this return type during explicit type symbol definition to ensure the operator integrates correctly with EK9's tri-state object model (absent/unset/set). Returning any type other than Boolean would break conditional logic that depends on checking object state. This operator is used extensively in guards, assertions, and control flow, making the Boolean return type non-negotiable.

Example (Error)

#!ek9
defines module bad.classes.operators.examples2

  defines class

    C2
      //The 'is set' operator
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_BOOLEAN
      override operator ? as pure
        <- rtn as Integer: 1

Solution

#!ek9
defines module bad.classes.operators.examples2

  defines class

    C2
      //The 'is set' operator must return Boolean
      override operator ? as pure
        <- rtn as Boolean: true

See Also


E07530: Only Compatible With Boolean

Classification: ONLY_COMPATIBLE_WITH_BOOLEAN

Description

Certain language constructs like assert require Boolean expressions because they fundamentally test true/false conditions. While EK9 allows checking if a value is set using the ? operator (assert int1?), directly asserting a non-Boolean type (assert int1) is not permitted. The compiler detects this during full resolution to prevent logical errors where developers might confuse value testing with Boolean conditions. Use the ? operator to convert non-Boolean values to Boolean for assertion.

Example (Error)

#!ek9
defines module bad.range.collection

  defines program
    CheckAssert()
      int1 <- 1
      boolean1 <- true
      boolean2 <- false

      //All of these will be fine.
      assert boolean1
      assert boolean2
      assert int1?

      //But this won't be fine, assert needs a boolean to check.
      @Error: FULL_RESOLUTION: ONLY_COMPATIBLE_WITH_BOOLEAN
      assert int1

Solution

#!ek9
defines module bad.range.collection

  defines program
    CheckAssert()
      int1 <- 1
      boolean1 <- true

      //Use ? operator to convert to Boolean
      assert int1?  //Checks if int1 is set
      assert boolean1  //Direct Boolean assertion

See Also


E07540: Must Be A Boolean

Classification: MUST_BE_A_BOOLEAN

Description

The ternary operator (<- condition <- value1 else value2) requires a Boolean control expression to determine which value to select. Using a non-Boolean type like String as the control expression is not permitted. The compiler detects this during full resolution because the ternary fundamentally performs conditional selection based on true/false evaluation. Unlike languages with truthy/falsy semantics, EK9 requires explicit Boolean types for conditional logic to prevent ambiguous behavior.

Example (Error)

#!ek9
defines module bad.control.types

  defines program
    TernaryWithNonBooleanControl()
      stringControl <- "Not Allowed"

      @Error: FULL_RESOLUTION: MUST_BE_A_BOOLEAN
      result1 <- stringControl <- "A" else "B"
      assert result1?

Solution

#!ek9
defines module bad.control.types

  defines program
    TernaryWithBooleanControl()
      booleanControl <- true

      //Use Boolean control expression
      result1 <- booleanControl <- "A" else "B"
      assert result1?

See Also


E07550: Must Return Integer

Classification: MUST_RETURN_INTEGER

Description

The spaceship comparison operator (<=>) must return an Integer to indicate ordering: negative for less-than, zero for equal, positive for greater-than. This three-way comparison result enables sorting algorithms and ordered collections. The compiler enforces this return type during explicit type symbol definition because the operator's semantic contract requires numeric ordering values. Returning any other type (like Boolean or the class itself) would break sorting and comparison logic that depends on the integer ordering convention.

Example (Error)

#!ek9
defines module bad.classes.operators.examples1

  defines class

    C1
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_INTEGER
      operator <=> as pure
        -> arg0 as C1
        <- rtn as C1: this

Solution

#!ek9
defines module bad.classes.operators.examples1

  defines class

    C1
      operator <=> as pure
        -> arg0 as C1
        <- rtn as Integer: 0  //Returns -1, 0, or 1 for ordering

See Also


E07560: Must Be Integer Greater Than Zero

Classification: MUST_BE_INTEGER_GREATER_THAN_ZERO

Description

Stream operations like head, tail, and skip require an Integer argument greater than zero because taking zero elements or skipping zero elements is semantically meaningless. The compiler detects this during full resolution to prevent logic errors. Negative values are also rejected because these operations work forward through streams - you cannot take or skip a negative count of elements. Use positive integers (1, 2, 3...) to specify how many elements to process.

Example (Error)

#!ek9
defines module bad.stream.headtailskip

  defines program
    InvalidStreamCatHead1()
      collection <- List() of String

      @Error: FULL_RESOLUTION: MUST_BE_INTEGER_GREATER_THAN_ZERO
      cat ["last", "first"] | head 0 > collection
      assert collection?

Solution

#!ek9
defines module bad.stream.headtailskip

  defines program
    ValidStreamCatHead()
      collection <- List() of String

      cat ["last", "first"] | head 1 > collection  //Must be >= 1
      assert collection?

See Also


E07570: Must Return String

Classification: MUST_RETURN_STRING

Description

The string representation operator ($) must return a String because it converts objects to their textual representation for display, logging, or serialization. The compiler enforces this return type during explicit type symbol definition to ensure the operator integrates correctly with string interpolation ("Value: $obj"), concatenation, and output operations. Returning any other type (like Integer or custom objects) would break string formatting that depends on the operator producing text.

Example (Error)

#!ek9
defines module bad.classes.operators.examples4

  defines class

    C4
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_STRING
      operator $ as pure
        <- rtn as Integer: 1

Solution

#!ek9
defines module bad.classes.operators.examples4

  defines class

    C4
      operator $ as pure
        <- rtn as String: "C4 instance"  //Returns String representation

See Also


E07580: Must Return JSON

Classification: MUST_RETURN_JSON

Description

The JSON representation operator ($$) must return a JSON type because it converts objects to structured JSON format for APIs, configuration files, and data interchange. The compiler enforces this return type during explicit type symbol definition to ensure the operator produces valid JSON structures that can be serialized, transmitted, and parsed. Returning String (even if it contains JSON text) is not permitted - the operator must return EK9's JSON type which provides structural guarantees and proper encoding.

Example (Error)

#!ek9
defines module bad.classes.operators.examples4

  defines class

    C4
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: MUST_RETURN_JSON
      operator $$ as pure
        <- rtn as String: ""

Solution

#!ek9
defines module bad.classes.operators.examples4

  defines class

    C4
      operator $$ as pure
        <- rtn as JSON: JSON()  //Returns JSON structure

See Also


E07590: Program Can Only Return Integer

Classification: PROGRAM_CAN_ONLY_RETURN_INTEGER

Description

EK9 programs (entry points) can only return Integer types, following the Unix/POSIX convention where programs communicate success or failure through integer exit codes (0 for success, non-zero for errors). The compiler enforces this during early symbol definition to prevent programs from returning incompatible types like Float, String, Boolean, or custom objects. This restriction enables proper integration with operating system process management and shell scripting, where exit codes must be integers. Programs that don't need to return a value can simply omit the return declaration.

Example (Error)

#!ek9
defines module bad.programs.returnparams

  defines program

    //Failure 1 - cannot return anything other than an Integer
    BadReturnType1()
      @Error: SYMBOL_DEFINITION: PROGRAM_CAN_ONLY_RETURN_INTEGER
      <- rtn as Float: 0.9
      //Just some body to stop any other compiler errors
      v <- "Steve"
      assert v?

Solution

#!ek9
defines module bad.programs.returnparams

  defines program

    GoodReturnType()
      <- rtn as Integer: 0  //Success exit code
      v <- "Steve"
      assert v?

See Also


E07600: Program Argument Type Invalid

Classification: PROGRAM_ARGUMENT_TYPE_INVALID

Description

Program arguments (command-line parameters) must use EK9 built-in types that can be easily parsed from string representations - String, Integer, Boolean, Float, Date, etc. Custom types like records, classes, functions, or complex data structures are not permitted as program arguments. The compiler enforces this during symbol definition because program arguments come from the command line as text and must be convertible from strings. Complex types would require custom parsing logic that belongs in the program body, not in the parameter list.

Example (Error)

#!ek9
defines module bad.programs.argumentschecks

  defines record
    ATestRecord
      first <- "A"
      second <- 9

  defines program

    //Failure2 - not supported, developer must take the List of String and build/parse them to create the record.
    BadArgumentType1()
      @Error: SYMBOL_DEFINITION: PROGRAM_ARGUMENT_TYPE_INVALID
      -> arg as ATestRecord
      //Just some body to stop any other compiler errors
      v <- "Steve"
      assert v?

Solution: Use Built-in Types

#!ek9
defines module bad.programs.argumentschecks

  defines program

    GoodArgumentType()
      ->
        firstName as String
        age as Integer
      //Build custom type from built-in arguments
      record <- ATestRecord(firstName, age)
      assert record?

See Also


E07610: Program Arguments Inappropriate

Classification: PROGRAM_ARGUMENTS_INAPPROPRIATE

Description

Program arguments must be either individual named parameters OR a single List of String for raw command-line access - mixing both is inappropriate. The compiler enforces this during symbol definition because these two approaches represent different argument processing strategies. Individual parameters (like port as Integer) provide type-safe, structured access. A single List of String provides raw access to all command-line arguments. Combining them creates ambiguous semantics about which arguments go where.

Example (Error)

#!ek9
defines module bad.programs.argumentschecks

  defines program

    //Failure1 - not supported, it's either list all the parameters or have a List of String
    BadArgumentCombination()
      ->
        arg1 as Integer
        @Error: SYMBOL_DEFINITION: PROGRAM_ARGUMENTS_INAPPROPRIATE
        arg2 as List of String

Solution 1: Use Individual Parameters

#!ek9
defines module bad.programs.argumentschecks

  defines program

    GoodIndividualParams()
      ->
        port as Integer
        host as String
        debug as Boolean

Solution 2: Use Raw Argument List

#!ek9
defines module bad.programs.argumentschecks

  defines program

    GoodRawArgs()
      -> args as List of String
      //Parse args manually as needed

See Also


E07860: Function Or Delegate Required

Classification: FUNCTION_OR_DELEGATE_REQUIRED

Description

A function or function delegate is required. Operations like map, filter, or reduce require functional arguments.

Example (Error)

#!ek9
defines function
  demo()
    numbers <- [1, 2, 3]
    @Error: FULL_RESOLUTION: FUNCTION_OR_DELEGATE_REQUIRED
    result <- cat numbers | map with 42  //Not a function

Solution

#!ek9
defines function
  doubler()
    -> value as Integer
    <- result as Integer: value * 2

defines function
  demo()
    numbers <- [1, 2, 3]
    result <- cat numbers | map with doubler | collect as List of Integer

See Also


E07870: Integer Var Or Function Or Delegate Required

Classification: INTEGER_VAR_OR_FUNCTION_OR_DELEGATE_REQUIRED

Description

An Integer value or function/function delegate is required. Some operations accept either a literal integer or a function that produces integers.

Example (Error)

#!ek9
defines function
  demo()
    @Error: FULL_RESOLUTION: INTEGER_VAR_OR_FUNCTION_OR_DELEGATE_REQUIRED
    result <- someOperation("not an integer")  //Need Integer or function

Solution 1: Use Integer Value

#!ek9
defines function
  demo()
    result <- someOperation(42)  //Integer value

Solution 2: Use Function

#!ek9
defines function
  getSize()
    <- result as Integer: 100

defines function
  demo()
    result <- someOperation(getSize)  //Function that returns Integer

See Also


E07880: Function Or Delegate Not Required

Classification: FUNCTION_OR_DELEGATE_NOT_REQUIRED

Description

A function or function delegate is not required here. The operation expects a value, not a function reference.

Example (Error)

#!ek9
defines function
  getValue()
    <- result as Integer: 42

defines function
  demo()
    @Error: FULL_RESOLUTION: FUNCTION_OR_DELEGATE_NOT_REQUIRED
    value <- 10 + getValue  //Should call the function

Solution: Call the Function

#!ek9
defines function
  getValue()
    <- result as Integer: 42

defines function
  demo()
    value <- 10 + getValue()  //Call the function

    //Or use the value directly
    directValue <- 52

See Also


Method and Modifier Errors

Method and modifier errors relate to method overriding, abstract methods, constructors, access modifiers, default operators, and implementation requirements. These ensure proper object-oriented design and inheritance in EK9.

E07010: Method Access Modifier Private Override

Classification: METHOD_ACCESS_MODIFIER_PRIVATE_OVERRIDE

Description

Combining 'private' with 'override' is contradictory and not allowed. Private methods are not visible to subclasses, making it impossible for them to override parent methods. The 'override' modifier requires method visibility (public, protected, or package-private) to enable polymorphic behavior. Additionally, if a parent method is private, it is not visible to subclasses and therefore cannot be overridden. This error is caught during symbol definition phase to prevent logical contradictions in method access semantics.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines class

    C0
      private basicMethod()
        <- rtn as String: "OK"

    C1 extends C0

      //Attempting to override a private method from parent
      @Error: SYMBOL_DEFINITION: METHOD_ACCESS_MODIFIER_PRIVATE_OVERRIDE
      override private basicMethod()
        <- rtn as String: "OK"

Solution: Remove Private or Override

#!ek9
defines class extends Base
  Derived
    //Option 1: Override without private
    override calculate()
      <- result as Integer: 100

    //Option 2: Private without override (different method)
    private internalCalculate()
      <- result as Integer: 100

See Also


E07020: Override And Abstract

Classification: OVERRIDE_AND_ABSTRACT

Description

Combining 'override' with 'abstract' is logically contradictory. The 'override' modifier indicates that a method is providing a concrete implementation for a parent class or trait method. The 'abstract' modifier indicates a method declaration without implementation, requiring subclasses to provide the implementation. A method cannot simultaneously provide an implementation (override) and declare it lacks an implementation (abstract). This applies to both regular methods and operators. The compiler enforces this during explicit type symbol definition to prevent logical inconsistencies in type hierarchies.

Example (Error)

#!ek9
defines module bad.abstractuse.example

  defines class

    C2 as abstract

      operator > as pure abstract
        -> arg0 as C2
        <- rtn as Boolean?

      //Attempting to override an operator but also declaring it abstract
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: OVERRIDE_AND_ABSTRACT
      override operator <= as pure abstract
        -> arg0 as C2
        <- rtn as Boolean?

Solution: Choose One

#!ek9
defines class extends Base
  Derived
    //Option 1: Provide implementation (override)
    override process()
      <- result as String: "Processed"

    //Option 2: Keep abstract (no override)
    abstract
    process()
      <- result as String

See Also


E07030: Default And Trait

Classification: DEFAULT_AND_TRAIT

Description

The 'default' operator keyword is not supported on traits. Default operators use compiler-generated implementations based on the type's structure (e.g., generating comparison operators from a `<=>` implementation). This feature requires concrete type information including property layouts and initialization semantics, which traits do not possess. Traits are abstract contracts that define behavior without implementation details. For operators on traits, you must either provide explicit implementations or declare them as abstract. This error is caught early during symbol definition phase to prevent invalid trait definitions.

Example (Error)

#!ek9
defines module earlybad.defaultoperators.examples

  defines trait

    T1

      operator <=> as pure
        -> arg0 as T1
        <- rtn as Integer: 0

      //Attempting to use default operator generation on a trait
      @Error: SYMBOL_DEFINITION: DEFAULT_AND_TRAIT
      default operator >=

Solution: Provide Full Implementation

#!ek9
defines trait
  Comparable
    operator == as pure  //Provide implementation
      -> arg0 as Comparable
      <- result as Boolean
        //Implementation logic

See Also


E07040: Trait By Identifier Not Supported

Classification: TRAIT_BY_IDENTIFIER_NOT_SUPPORTED

Description

The 'by' delegation identifier is not supported when composing traits. Delegation using 'by' is a class-specific feature that forwards method calls to a contained property implementing a trait. Traits themselves cannot have properties (they're purely behavioral contracts), so they cannot delegate to property instances. When composing a trait from multiple other traits using `with trait of T1, T2`, you cannot specify delegation identifiers. Classes implementing the composite trait can use delegation, but trait definitions cannot. This ensures traits remain pure behavioral abstractions without state.

Example (Error)

#!ek9
defines module trait.with.trait.by

  defines trait
    T1
      methodOne()
        <- rtn <- true

    T2
      methodTwo()
        <- rtn <- false

    //Traits cannot delegate - they have no properties
    @Error: FULL_RESOLUTION: TRAIT_BY_IDENTIFIER_NOT_SUPPORTED
    Composite with trait of T1 by t1, T2

Solution: Use in Class

#!ek9
defines trait
  Printable
    print()
      -> value as String

defines class with trait of Printable
  Document
    printer as Printer: Printer()
    by printer  //'by' delegation in class

See Also


E07050: Abstract Constructor

Classification: ABSTRACT_CONSTRUCTOR

Description

Declaring a constructor as 'abstract' is logically contradictory and not allowed. Constructors are responsible for initializing new object instances, which requires concrete implementation code to set initial property values and establish object state. Abstract declarations indicate "no implementation provided, subclasses must implement", but constructors are not inherited in the same way as methods - each class must provide its own initialization logic. The concept of an abstract constructor makes no sense because you cannot create instances of a type without concrete initialization code. This error is caught during symbol definition phase to prevent fundamentally flawed type definitions.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines class

    C1

      p1 as String?
      p2 as Integer?

      //Constructors cannot be abstract - they must initialize objects
      @Error: SYMBOL_DEFINITION: ABSTRACT_CONSTRUCTOR
      C1() as abstract
        -> param as Integer

Solution: Provide Implementation

#!ek9
defines class
  MyClass
    value as Integer: 0

    MyClass()  //Remove abstract
      -> initialValue as Integer
      value := initialValue

See Also


E07060: Override Constructor

Classification: OVERRIDE_CONSTRUCTOR

Description

Using 'override' modifier on constructors is not allowed. While it's natural for developers from other object-oriented languages to think they are "overriding" a parent constructor, EK9 does not express constructor inheritance this way. Constructors are not inherited like methods - each class defines its own initialization logic. Even when calling `super()` to initialize the parent class, you are not overriding the parent constructor, you are delegating initialization to it. The 'override' modifier is specifically for methods and operators that participate in polymorphism, which does not apply to constructors. This error is caught during symbol definition to prevent conceptual confusion about constructor semantics.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines class

    C1

      p1 as String?
      p2 as Integer?

      //Cannot use override on constructors
      @Error: SYMBOL_DEFINITION: OVERRIDE_CONSTRUCTOR
      override C1()
        ->
          p1 as String
          p2 as Integer
        super()
        this.p1 = p1
        this.p2 = p2

Solution: Remove Override

#!ek9
defines class extends BaseClass
  DerivedClass
    DerivedClass()  //No 'override' needed
      super()

See Also


E07070: Traits Do Not Have Constructors

Classification: TRAITS_DO_NOT_HAVE_CONSTRUCTORS

Description

Traits cannot have constructors. Traits are pure behavioral contracts that define method signatures and optionally provide default implementations. Unlike classes, traits cannot be directly instantiated - they must be implemented by classes or other aggregates. Since traits cannot create instances, constructor methods make no sense and are not allowed. Initialization logic belongs in the classes that implement the trait, not in the trait itself. This restriction ensures traits remain focused on defining behavior without concerning themselves with object instantiation. This error is caught during symbol definition to prevent invalid trait definitions.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines trait
    T1
      //Traits cannot have constructors
      @Error: SYMBOL_DEFINITION: TRAITS_DO_NOT_HAVE_CONSTRUCTORS
      T1()

Solution: Use Initialization Methods

#!ek9
defines trait
  Nameable
    name as String: ""

    setName()  //Use method instead
      -> newName as String
      name := newName

See Also


E07080: Invalid Default Constructor

Classification: INVALID_DEFAULT_CONSTRUCTOR

Description

The 'default' constructor modifier can only be used on parameterless constructors. In EK9, 'default' has a specific meaning: it marks the zero-argument constructor that can be automatically invoked during object initialization. A constructor with parameters cannot be a default constructor because there would be no way to automatically determine what argument values to provide. If you need a constructor with parameters, define it as a regular (non-default) constructor. The compiler enforces this during symbol definition to ensure clear and unambiguous object initialization semantics.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines class

    C1

      //Default constructor cannot have parameters
      @Error: SYMBOL_DEFINITION: INVALID_DEFAULT_CONSTRUCTOR
      default C1()
        -> param as String

Solution

#!ek9
defines class
  MyClass
    value as Integer: 0

    default MyClass()  //No parameters for default

    MyClass()  //Regular constructor with parameters
      -> initialValue as Integer
      value := initialValue

See Also


E07090: Default Only For Constructors

Classification: DEFAULT_ONLY_FOR_CONSTRUCTORS

Description

The 'default' modifier is only valid for constructors and operators, not regular methods. For constructors, 'default' marks the parameterless constructor that can be automatically invoked. For operators, 'default' requests compiler-generated implementations based on other operators (e.g., generating `>=` from `<=>`). Regular methods must be explicitly implemented and cannot use 'default'. This restriction prevents confusion about what 'default' would mean for a method (there is no "default implementation" that can be automatically generated for arbitrary methods). This error is caught during symbol definition to ensure proper modifier usage.

Example (Error)

#!ek9
defines module bad.default.methods.examples

  defines class
    C1
      //Valid: default constructor
      default C1()

      //Invalid: default on regular method
      @Error: SYMBOL_DEFINITION: DEFAULT_ONLY_FOR_CONSTRUCTORS
      default normalMethod()
        -> arg0 as String
        <- rtn as String: String(arg0)

Solution: Remove Default

#!ek9
defines class
  MyClass
    process()  //Remove 'default'
      <- result as String: "Processed"

    default MyClass()  //'default' OK for constructors

See Also


E07100: Abstract But Body Provided

Classification: ABSTRACT_BUT_BODY_PROVIDED

Description

A method or operator declared as 'abstract' cannot have an implementation body. The 'abstract' modifier means "this declares a contract that subclasses must implement" - it explicitly indicates no implementation is provided. Providing a body (return value initialization or method logic) contradicts this declaration. This error applies to both regular methods and operators. If you want to provide an implementation, remove the 'abstract' modifier. If you want to require subclasses to implement it, remove the body. This error is caught during explicit type symbol definition when the compiler validates operator and method signatures.

Example (Error)

#!ek9
defines module bad.abstractuse.example

  defines class

    C2 as abstract

      //Declaring operator as abstract but providing implementation
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: ABSTRACT_BUT_BODY_PROVIDED
      operator < as pure abstract
        -> arg0 as C2
        <- rtn as Boolean: true

Solution: Remove Abstract or Body

#!ek9
defines class
  MyClass
    //Option 1: Remove abstract, keep implementation
    process()
      <- result as String: "Implementation"

    //Option 2: Keep abstract, remove body
    abstract
    calculate()
      <- result as Integer

See Also


E07110: Not Abstract And No Body Provided

Classification: NOT_ABSTRACT_AND_NO_BODY_PROVIDED

Description

A method or operator without an implementation body must be explicitly declared as 'abstract'. In EK9, every method and operator must either provide a concrete implementation (body) or be marked abstract to indicate that subclasses must provide the implementation. Unlike traits (which are implicitly abstract), class methods and operators must be explicit about their abstract nature. This prevents accidental omissions where a developer forgot to implement a method - the compiler requires you to consciously declare it abstract if no body is provided. This error is caught during explicit type symbol definition when validating operator and method completeness.

Example (Error)

#!ek9
defines module bad.abstractuse.example

  defines class

    C2 as abstract

      //Operator without body must be declared abstract
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: NOT_ABSTRACT_AND_NO_BODY_PROVIDED
      operator >= as pure
        -> arg0 as C2
        <- rtn as Boolean?

Solution

#!ek9
defines class
  MyClass
    //Option 1: Provide implementation
    process()
      <- result as String: "Processed"

    //Option 2: Declare as abstract
    abstract
    calculate()
      <- result as Integer

See Also


E07120: Dispatcher But No Body Provided

Classification: DISPATCHER_BUT_NO_BODY_PROVIDED

Description

Dispatcher methods must provide a base implementation body. In EK9, 'dispatcher' methods use dynamic method resolution to call different method overloads based on actual parameter types at runtime. Unlike abstract methods which defer implementation to subclasses, dispatcher methods must provide a fallback implementation that executes when no specific overload matches the runtime types. This base implementation serves as the entry point that dispatches to specialized methods, or handles cases where no specialized method exists. The requirement for a body ensures dispatchers are complete and functional. This error is caught during symbol definition to enforce proper dispatcher structure.

Example (Error)

#!ek9
defines module bad.classes.examples

  defines class

    BadDispatcher1

      //Dispatcher method without implementation body
      @Error: SYMBOL_DEFINITION: DISPATCHER_BUT_NO_BODY_PROVIDED
      process() as dispatcher

Solution: Provide Implementation

#!ek9
defines class
  RequestHandler
    @Dispatcher
    process()
      -> request
      <- result as Boolean: true  //Provide default implementation

    handleGet()
      -> request as GetRequest
      <- result as Boolean: true

    handlePost()
      -> request as PostRequest
      <- result as Boolean: true

See Also


E07130: Not Marked Abstract But Is Abstract

Classification: NOT_MARKED_ABSTRACT_BUT_IS_ABSTRACT

Description

A class that contains abstract methods or operators must be explicitly marked as 'abstract'. When a class implements a trait with abstract members, or defines abstract members itself, it becomes abstract and cannot be directly instantiated. EK9 requires you to explicitly declare the class as abstract to make this clear to developers using the class. This prevents accidental attempts to instantiate classes that lack complete implementations. This error is caught during full resolution after all traits, inheritance, and method overrides are fully analyzed.

Example (Error)

#!ek9
defines module bad.overridden.classoperators

  defines trait
    T1
      operator $ as pure abstract
        <- rtn as String?

  //Class implements trait with abstract operator but is not marked abstract
  @Error: FULL_RESOLUTION: NOT_MARKED_ABSTRACT_BUT_IS_ABSTRACT
  C2 with trait of T1

    operator #? as pure
      <- rtn as Integer: 0
    default operator ?

Solution: Mark Class as Abstract

#!ek9
defines class
  abstract MyClass  //Add 'abstract'
    abstract
    process()
      <- result as String

See Also


E07140: Dynamic Class Must Implement Abstracts

Classification: DYNAMIC_CLASS_MUST_IMPLEMENT_ABSTRACTS

Description

Dynamic classes must provide implementations for all abstract methods and operators from their base classes and traits. When you create a dynamic class using `() with trait of T as class` or extend an abstract class, EK9 requires complete implementation of all abstract members at the point of creation. Unlike static classes where abstract members can be inherited and implemented later in subclasses, dynamic classes are concrete instances that must be immediately usable. This ensures type safety and prevents runtime errors from calling unimplemented methods. This error is caught during full resolution after analyzing all traits and method requirements.

Example (Error)

#!ek9
defines module fuzz.dynamic.missing.trait.methods

  defines trait
    Processor
      process() as abstract
        -> input as String
        <- output as String?

      validate() as abstract
        -> input as String
        <- valid as Boolean?

  defines program

    TestMissingTraitMethods()

      //Missing implementation of validate() method
      @Error: FULL_RESOLUTION: DYNAMIC_CLASS_MUST_IMPLEMENT_ABSTRACTS
      processor <- () with trait of Processor as class
        override process()
          -> input as String
          <- output as String: "processed: " + input

        //validate() method not implemented - ERROR

Solution: Implement All Abstracts

#!ek9
defines function
  demo()
    instance <- BaseClass() with
      value as Integer: 42
      override process()  //Implement abstract method
        <- result as String: "Processed"

See Also


E07150: Text Method Missing

Classification: TEXT_METHOD_MISSING

Description

When defining text constructs for multiple language locales, all locales must have identical method signatures. EK9's text system allows you to define locale-specific text methods using `defines text for "en_GB"` and `defines text for "de"` for example. Every method defined in one locale must exist with the same signature (parameters and return type) in all other locales. This ensures that code can call text methods without knowing which locale is active - all locales provide the same interface. Missing methods cause runtime errors when the locale switches, so the compiler enforces completeness. This error is caught during full resolution after analyzing all text construct definitions.

Example (Error)

#!ek9
defines module bad.missingtextmethods.examples1

  defines text for "en_GB"

    //Missing namedWelcome(name as String) method present in German locale
    @Error: FULL_RESOLUTION: TEXT_METHOD_MISSING
    WelcomePageText

      namedWelcome()
        -> person as Person
        `Welcome ${person.firstName}`

  defines text for "de"

    WelcomePageText
      namedWelcome()
        -> person as Person
        `Willkommen ${person.firstName}`

      //This method exists in German but not in English
      namedWelcome()
        -> name as String
        `Willkommen ${name}`

Solution: Add Missing Method to All Locales

#!ek9
defines module bad.missingtextmethods.examples1

  defines text for "en_GB"

    WelcomePageText
      namedWelcome()
        -> person as Person
        `Welcome ${person.firstName}`

      //Add the missing method that exists in German locale
      namedWelcome()
        -> name as String
        `Welcome ${name}`

  defines text for "de"

    WelcomePageText
      namedWelcome()
        -> person as Person
        `Willkommen ${person.firstName}`

      namedWelcome()
        -> name as String
        `Willkommen ${name}`

See Also


E07160: Implementation Must Be Provided

Classification: IMPLEMENTATION_MUST_BE_PROVIDED

Description

Certain EK9 constructs must always have complete implementations and cannot be abstract or unimplemented. Programs (main entry points), functions, and service methods must all have bodies. Unlike classes which can have abstract methods, these constructs are executable entry points that must provide concrete behavior. A program without a body cannot run, a function without implementation cannot be called, and a service without implementation cannot handle HTTP requests. This restriction ensures all callable constructs are complete and functional. This error is caught during symbol definition to prevent fundamentally incomplete definitions.

Example (Error)

#!ek9
defines module bad.programs.examples

  defines program

    //Programs must have implementation bodies
    @Error: SYMBOL_DEFINITION: IMPLEMENTATION_MUST_BE_PROVIDED
    Program4()

Solution: Provide Implementation

#!ek9
defines function
  calculate()
    -> value as Integer
    <- result as Integer: value * 2  //Provide implementation

See Also


E07170: Explicit Constructor Required

Classification: EXPLICIT_CONSTRUCTOR_REQUIRED

Description

An explicit constructor must be provided when a class has properties that are never initialized. EK9 requires all properties to be initialized either through inline initialization (`prop <- value`), or through constructor initialization. When properties are declared without inline values and are not initialized in any constructor, the compiler cannot generate safe initialization code. You must provide an explicit constructor that initializes all such properties. This ensures object instances are never in an invalid state with uninitialized properties. This error is caught during pre-IR checks after analyzing all property initialization flows.

Example (Error)

#!ek9
defines module complexity.class.never.init

  defines class

    //Class with uninitialized property requires explicit constructor
    @Error: PRE_IR_CHECKS: EXPLICIT_CONSTRUCTOR_REQUIRED
    ClassWithUninitializedProps
      @Error: PRE_IR_CHECKS: NEVER_INITIALISED
      prop1 as String?

      useProperty()
        @Error: PRE_IR_CHECKS: NOT_INITIALISED_BEFORE_USE
        result <- prop1 + " suffix"
        assert result?

Solution: Provide Constructor That Initializes Properties

#!ek9
defines module complexity.class.never.init

  defines class

    ClassWithUninitializedProps
      prop1 as String?

      //Constructor initializes the uninitialized property
      ClassWithUninitializedProps()
        -> initialValue as String
        prop1 :=? initialValue  //Initialize property in constructor

      useProperty()
        result <- prop1 + " suffix"
        assert result?

See Also


E07180: Missing Operator In This

Classification: MISSING_OPERATOR_IN_THIS

Description

When using 'default operator' to auto-generate operator implementations, this type must already define certain foundational operators. For example, defaulting comparison operators (`<`, `<=`, `>`, `>=`) requires the `<=>` operator to be defined or also defaulted. The compiler uses existing operator implementations to generate the requested default operators. If the required foundation operators are missing from this type, default generation cannot proceed. You must either provide the missing operator implementation yourself, or also default it if possible. This ensures all generated operators have valid implementations. This error is caught during full resolution when analyzing operator dependencies.

Example (Error)

#!ek9
defines module bad.defaulted.classoperators

  defines class

    SomeClass2
      prop1 <- Time()

      //Cannot default < operator without <=> operator
      @Error: FULL_RESOLUTION: MISSING_OPERATOR_IN_THIS
      default operator <

Solution: Define the Operator

#!ek9
defines class
  MyClass
    value as Integer: 0

    operator == as pure
      -> arg0 as MyClass
      <- result as Boolean: value == arg0.value

    default operator <>  //Now can use default

See Also


E07190: Missing Operator In Super

Classification: MISSING_OPERATOR_IN_SUPER

Description

When using 'default operator' in a subclass, the parent class must define the operator being defaulted. Default operator generation in subclasses first delegates to the parent class's operator implementation, then applies the operator to the subclass's additional properties. This ensures inherited properties are properly included in operator behavior (e.g., equality checking all properties including inherited ones). If the parent class lacks the required operator, default generation cannot create a complete implementation. You must add the operator to the parent class, or provide an explicit implementation in the subclass. This error is caught during full resolution when analyzing inheritance hierarchies and operator availability.

Example (Error)

#!ek9
defines module bad.defaulted.classoperators

  defines class

    MinimalBase1 as open
      prop1 <- String()

    //Parent class lacks operators needed for default generation
    Invalid1 is MinimalBase1
      prop2 <- String()

      @Error: FULL_RESOLUTION: MISSING_OPERATOR_IN_SUPER
      default operator

Solution: Define in Parent

#!ek9
defines class
  Base
    operator == as pure
      -> arg0 as Base
      <- result as Boolean: true

defines class extends Base
  Derived
    default operator ==  //Now valid

See Also


E07200: Missing Operator In Property Type

Classification: MISSING_OPERATOR_IN_PROPERTY_TYPE

Description

When using 'default operator' to generate implementations based on properties, all property types must define the operator being defaulted. Default operator generation works by applying the operator to each property in order of definition. For example, defaulting the `==` operator generates code that compares each property using `==`. If any property's type lacks the required operator, the generated code would be invalid. You must ensure all property types implement the necessary operators before using default operator generation. This error is caught during full resolution when analyzing property types and their available operators.

Example (Error)

#!ek9
defines module bad.defaulted.classoperators

  defines class

    SomeClass1
      prop1 <- Time()
      //No operators defined

    //Property type SomeClass1 lacks operators for default generation
    Invalid3
      prop1 <- SomeClass1()

      @Error: FULL_RESOLUTION: MISSING_OPERATOR_IN_PROPERTY_TYPE
      default operator

Solution: Define Operator in Property Type

#!ek9
defines class
  Inner
    operator == as pure
      -> arg0 as Inner
      <- result as Boolean: true

defines class
  Outer
    inner as Inner: Inner()
    default operator ==  //Now valid

See Also


E07210: Function Delegate With Default Operators

Classification: FUNCTION_DELEGATE_WITH_DEFAULT_OPERATORS

Description

Records containing function delegate properties cannot use default operators for comparison operators like `<=>`. Function delegates are references to executable code, not data values. Comparing function delegates for ordering (`<=>`) has no meaningful semantic definition - there's no natural ordering of function references. Default operator generation relies on property-by-property comparison, but function delegates don't support comparison operations. If a record needs comparison operators and contains function delegates, those operators must be explicitly implemented to compare non-delegate properties only. This ensures comparisons have clear semantics.

Example (Error)

#!ek9
defines module functiondelegate.inrecord.withgeneric

  defines record

    R1
      delegate as TestFunction?

      default operator ?

      //Cannot default <=> operator with function delegate property
      @Error: FULL_RESOLUTION: FUNCTION_DELEGATE_WITH_DEFAULT_OPERATORS
      default operator <=>

Solution: Define Explicitly

#!ek9
defines function
  MyFunction
    operator == as pure  //Define explicitly
      -> arg0 as MyFunction
      <- result as Boolean: true

See Also


E07220: Operator Default Not Supported

Classification: OPERATOR_DEFAULT_NOT_SUPPORTED

Description

Only specific operators support default generation - primarily comparison and equality operators that can be derived from the `<=>` operator, along with `?`, `$`, `$$`, and `#?`. Operators like `#<`, `#>`, `empty`, `contains`, `and`, `or`, and `xor` have semantics that cannot be automatically generated from property comparisons. These operators require custom logic specific to each type's behavior. For example, `contains` checks membership which is type-specific. The compiler restricts default generation to operators with well-defined automatic implementations. This is enforced during symbol definition to prevent invalid operator declarations.

Example (Error)

#!ek9
defines module earlybad.defaultoperators.examples

  defines record
    R1
      property1 <- String()

      //Operator 'empty' cannot be defaulted
      @Error: SYMBOL_DEFINITION: OPERATOR_DEFAULT_NOT_SUPPORTED
      default operator empty

Solution: Provide Implementation

#!ek9
defines class
  MyClass
    value as Integer: 0

    operator ++  //Implement explicitly
      value++

See Also


E07230: Default With Operator Signature

Classification: DEFAULT_WITH_OPERATOR_SIGNATURE

Description

The 'default' keyword for operators must appear alone without modifiers, parameters, return types, or body. Default operators request compiler-generated implementations based on existing operators and properties. Providing a signature (parameters, return type) or modifiers (pure, abstract) contradicts the purpose of 'default' - you're either asking the compiler to generate it (default) or providing it yourself (full signature). Mixing both creates ambiguity about whether to use your signature or generate one. The compiler requires clear intent: either `default operator X` for generation, or a complete operator definition. This is enforced during symbol definition to prevent ambiguous operator declarations.

Example (Error)

#!ek9
defines module earlybad.defaultoperators.examples

  defines record
    R1
      property1 <- String()

      //Default operator cannot have signature elements
      @Error: SYMBOL_DEFINITION: DEFAULT_WITH_OPERATOR_SIGNATURE
      default operator <= as pure
        -> arg0 as R1
        <- rtn as Boolean: false

Solution: Use Default Without Signature

#!ek9
defines class
  MyClass
    value as Integer: 0

    operator == as pure  //Define fully
      -> arg0 as MyClass
      <- result as Boolean: value == arg0.value

    default operator <>  //Just default, no signature

See Also


E07240: Method Modifier Protected In Service

Classification: METHOD_MODIFIER_PROTECTED_IN_SERVICE

Description

Protected access modifier is not valid in services. Services are web service endpoints where all operations must be either publicly accessible (for HTTP endpoints) or private (for internal helper methods). The protected modifier, designed for class inheritance, has no meaningful application in service context since services cannot be extended. Only private and public modifiers are supported.

Example (Error)

#!ek9
defines module bad.duplicate.and.modifier.service.methods

  defines service

    S4 for :/site4
      //This is OK
      private index1()
        <- rtn <- 0

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_PROTECTED_IN_SERVICE
      protected index2()
        <- rtn <- 0

Solution: Use Public or Private

#!ek9
defines service
  MyService
    GET for "/api/data"
      <- result as HTTPResponse: HTTPResponse().ok("Data")

    private helper()  //Use private
      <- result as String: "Helper"

See Also


E07250: Method Modifier Protected In Component

Classification: METHOD_MODIFIER_PROTECTED_IN_COMPONENT

Description

Protected access modifier is not valid in components. Components are dependency injection containers designed for composition, not inheritance. The protected modifier, which enables subclass access in class hierarchies, has no meaningful application in component architecture. Components can only use public methods (for external interfaces) or private methods (for internal implementation). Use private for internal helpers.

Example (Error)

#!ek9
defines module bad.duplicate.and.modifier.component.methods

  defines component

    S2
      //This is OK
      private index1()
        <- rtn <- 0

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_PROTECTED_IN_COMPONENT
      protected index2()
        <- rtn <- 0

Solution: Use Public or Private

#!ek9
defines component
  MyComponent
    process()  //Public
      <- result as String: helper()

    private helper()  //Use private
      <- result as String: "Helper"

See Also


E07260: Method Modifier Protected In Closed Class

Classification: METHOD_MODIFIER_PROTECTED_IN_CLOSED_CLASS

Description

Protected access modifier is not valid in closed (final/sealed) classes. The protected modifier exists specifically to enable subclass access in inheritance hierarchies. Since closed classes cannot be extended by design, declaring protected methods is contradictory and meaningless. If a method needs restricted visibility, use private. Classes are closed by default in EK9 unless explicitly marked "as open".

Example (Error)

#!ek9
defines module bad.inherited.classes

  defines class

    Class17
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_PROTECTED_IN_CLOSED_CLASS
      protected someMethod()
        var <- 1
        assert var?

Solution: Use Public or Private

#!ek9
defines class
  closed MyClass
    process()  //Public
      <- result as String: helper()

    private helper()  //Use private
      <- result as String: "Helper"

See Also


E07270: Method Modifier Not Required In Trait

Classification: METHOD_MODIFIER_NOT_REQUIRED_IN_TRAIT

Description

Access modifiers (public, private, protected) are not supported in traits. Traits define contracts and behavior that implementing classes/types must fulfill. All trait methods are implicitly public since they form the public contract of the trait. The concepts of private or protected visibility have no meaningful application in traits, as traits cannot have implementation-only methods - they exist solely to define interfaces that other types will implement or extend.

Example (Error)

#!ek9
defines module bad.duplicate.traitmethods

  defines trait

    T4
      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: METHOD_MODIFIER_NOT_REQUIRED_IN_TRAIT
      private traitsDoNotAllowPrivateMethods()
        -> arg0 as Integer
        <- rtn <- 1

Solution: Remove Access Modifier

#!ek9
defines trait
  Printable
    print()  //No access modifier needed
      -> value as String

See Also


E07280: Method Access Modifier Default

Classification: METHOD_ACCESS_MODIFIER_DEFAULT

Description

The explicit 'public' access modifier is not required in EK9 - methods are public by default unless marked private or protected. While this may seem counterintuitive coming from languages like Java where 'public' is commonly written, EK9 treats explicit 'public' as redundant noise. This error specifically catches the common mistake of developers migrating from Java/C++ who habitually write 'public' on methods. Simply omit the keyword - the method is already public.

Example (Error)

#!ek9
defines module bad.classmodifier.use

  defines class

    C1 extends C0
      @Error: SYMBOL_DEFINITION: METHOD_ACCESS_MODIFIER_DEFAULT
      public someMethod()
        <- rtn as String: "OK"

Solution: Specify Access Modifier

#!ek9
defines class
  MyClass
    public process()  //Add explicit modifier
      <- result as String: "Processed"

    private helper()  //Also explicit
      <- result as String: "Helper"

See Also


E07290: Records Only Support Constructor And Operator Methods

Classification: RECORDS_ONLY_SUPPORT_CONSTRUCTOR_AND_OPERATOR_METHODS

Description

Records are restricted to constructors and operators only - regular methods are not permitted. Records are designed as immutable data carriers that hold state and implement value semantics through operators (like equality, comparison, hashing). Regular methods that would transform or compute based on record data should be implemented as external functions that take the record as a parameter, maintaining the clear separation between data (records) and behavior (functions). This enforces functional design patterns and prevents records from becoming stateful objects.

Example (Error)

#!ek9
defines module bad.duplicate.recordmethods

  defines record

    R2
      someField <- 1

      @Error: EXPLICIT_TYPE_SYMBOL_DEFINITION: RECORDS_ONLY_SUPPORT_CONSTRUCTOR_AND_OPERATOR_METHODS
      private anotherMethodName()
        var <- "Steve"
        assert var?

Solution: Use Operators or Functions

#!ek9
defines record
  Point
    x as Integer
    y as Integer

    operator + as pure  //Operators OK
      -> other as Point
      <- result as Point: Point(x + other.x, y + other.y)

defines function
  movePoint()  //Use external function
    ->
      point as Point
      deltaX as Integer
      deltaY as Integer
    

See Also


E07300: Declared As Null Not Needed

Classification: DECLARED_AS_NULL_NOT_NEEDED

Description

Using the nullable marker '?' on parameters, variables, or return types is redundant and not needed in EK9. The '?' suffix is typically used for optional types (like Optional<String>), but EK9's tri-state semantics (absent/unset/set) mean that all reference types already support being unset without explicit '?' notation. For parameters and variables, declaring them without '?' is sufficient - they can be unset by default. The compiler enforces this to avoid confusion between nullable declarations and Optional types.

Example (Error)

#!ek9
defines module bad.variableonly.use

  defines function

    test1()
      @Error: SYMBOL_DEFINITION: DECLARED_AS_NULL_NOT_NEEDED
      -> var as String?
      <- rtn as String: String()

Solution: Use Optional or Omit

#!ek9
defines function
  demo()
    //Option 1: Use Optional
    value as Integer?

    //Option 2: Provide value
    count as Integer: 0

    //Option 3: Use variable with isSet semantics
    name as String  //Unset by default

See Also


🎉 All 215 EK9 compiler errors are now documented!

Every error includes detailed descriptions, working examples, complete solutions, and cross-references to help you quickly understand and fix compilation issues.

Need help with an error not listed here? Check the EK9 GitHub Issues or consult the relevant language documentation pages linked from the navigation menu.