E07236: dynamic class with captured variables requires 'default operator ?' to define set/unset semantics for captured fields

Category: E07xxx Full resolution · Reported in phase: FULL_RESOLUTION · Classification: DYNAMIC_CLASS_CAPTURES_REQUIRE_ISSET · All error codes

What went wrong

Your dynamic class captures variables from its enclosing scope, and those captured variables become private fields on the generated class. Since the class now has fields, it must define its own '?' (isSet) operator to support EK9's tri-state semantics.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 93 (How do I define a class in EK9?)

Quick fix

Add 'default operator ?' among the dynamic class's operators (after its methods) for the generated behaviour, true only when EVERY captured field is set - good.ek9 in the [EXAMPLE] section below. Or write 'override operator ? as pure' yourself when only some captures matter - good_custom.ek9 below.

Why captures are fields

A dynamic class that captures a variable - '(prefix) trait of Printable as class' - is compiled to a class with a private field for each capture ('prefix' becomes a field of type String). Because the class now has a field, the '?' it inherits from Any cannot inspect it. You must define '?' so that guards, safe access, and coalescing work correctly.

Multiple captures

Each captured variable becomes a separate field, and the generated '?' checks every one of them: with captures (prefix, suffix, separator), 'default operator ?' is true only when prefix?, suffix? and separator? are all true.

Custom operator implementation

If 'every capture set' does not suit your needs, write the operator yourself. In good_custom.ek9 below the dynamic class captures a host, a port and a timeout, and its '?' requires only the host and the port: the timeout is optional.

[MULTI-TRAIT DELEGATION] With 'by' delegation the delegated variables are captures too, so they also become private fields and the dynamic class needs its '?' (good_delegation.ek9 below).

How to fix it

Add ONE of these among the dynamic class's operators, after its method overrides:

  1. 'default operator ?' - the compiler generates it from the captured fields.
  2. 'override operator ? as pure' with '<- rtn as Boolean:' and your own logic.

Use 'default' when "every captured field set = object set" is what you want. Use 'override' when you need custom logic (e.g., only required captures matter).

See also

  • E07235: Full explanation of tri-state semantics and why aggregates need operator ?
  • E07180: default operator missing prerequisite in same class
  • E07190: default operator missing in super class

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E07236: dynamic class with captured variables requires 'default operator ?' to define set/unset semantics for captured fields

#!ek9
defines module error.e07236.bad

  defines trait

    Printable
      describe() as abstract
        <- result as String?

    Formatter
      format() as abstract
        -> items as List of String
        <- result as String?

  defines function

    testUnnamedCapture()
      prefix <- "Hello"

      //Single unnamed capture — prefix becomes a private field
      printer <- (prefix) trait of Printable as class
        override describe()
          <- result as String: prefix
        //Missing: default operator ?

    testNamedCapture()
      greeting <- "Hi"

      //Named capture — greeting becomes a private field
      printer <- Greeter(greeting) trait of Printable as class
        override describe()
          <- result as String: greeting
        //Missing: default operator ?

    testMultipleCaptures()
      prefix <- "["
      suffix <- "]"

      //Multiple captures — both become private fields
      formatter <- (prefix, suffix) trait of Printable as class
        override describe()
          <- result as String: prefix + suffix
        //Missing: default operator ?

    testCaptureWithOperatorPresent()
      prefix <- "OK"

      //This is valid — default operator ? is defined
      printer <- (prefix) trait of Printable as class
        override describe()
          <- result as String: prefix
        default operator ?
      require printer?

    testNoCapturesNoError()

      //No captures — no fields — no ? required
      printer <- () trait of Printable as class
        override describe()
          <- result as String: "static"
        default operator ?
      require printer?

Fixed: Add 'default operator ?' to the dynamic class

#!ek9
defines module error.e07236.good

  defines trait

    Printable
      describe() as abstract
        <- result as String?

  defines program

    PrintGreeting()
      stdout <- Stdout()
      prefix <- "Hello"

      //The capture becomes a field, so the dynamic class says when it is set
      printer <- (prefix) trait of Printable as class
        override describe()
          <- result as String: prefix
        default operator ?

      stdout.println(printer.describe())

Fixed: With 'by' delegation the captures are fields too

#!ek9
defines module error.e07236.good.delegation

  defines trait

    LengthCheck
      longEnough() as abstract
        -> text as String
        <- rtn as Boolean?

    CharacterCheck
      allLetters() as abstract
        -> text as String
        <- rtn as Boolean?

  defines class

    MinimumLength with trait of LengthCheck
      override longEnough()
        -> text as String
        <- rtn as Boolean: not text.empty()

    Letters with trait of CharacterCheck
      override allLetters()
        -> text as String
        <- rtn as Boolean: text matches /^[a-zA-Z]+$/

  defines program

    Validate()
      stdout <- Stdout()
      lengthCheck <- MinimumLength()
      characterCheck <- Letters()

      //Delegated 'by' captures: they are fields too, so '?' is defaulted
      validator <- (lengthCheck, characterCheck) trait of LengthCheck by lengthCheck, CharacterCheck by characterCheck
        default operator ?

      stdout.println($validator.longEnough("Steve"))

Fixed: Write the '?' yourself: only some captures are required

#!ek9
defines module error.e07236.good.custom

  defines trait
    Connectable
      connect() as abstract
        <- rtn as Boolean?

  defines program
    Connect()
      stdout <- Stdout()
      host <- "localhost"
      port <- 8080
      timeout <- Integer()

      //'set' means the host and port are set; the timeout is optional
      connection <- (host, port, timeout) trait of Connectable as class
        override connect()
          <- rtn as Boolean: host? and port? and (timeout? or port > 0)
        override operator ? as pure
          <- rtn as Boolean: host? and port?

      ready <- connection?
      stdout.println($ready)

Questions and answers

These Q&A entries show E07236 and its fix: