What does EK9 error E04050 mean when I declare a class field and how do I fix it?

← Classes and OOP · Ref: Q1282

E04050 fires when you use the '<-' declaration form for a class field with an expression the compiler cannot resolve to a simple type during early phase processing. In practice the usual cause is writing the is-set operator '?' at the end of a parameterised constructor call, like 'names <- List() of String?'. That is NOT a nullable-type marker in EK9 — it is the is-set operator being invoked on the newly-created List, which produces a Boolean, which cannot be the type of a List field. The compiler reports E04050 because type inference cannot reconcile 'List of String' with 'Boolean'.

THE FIX

Drop the trailing '?' and use the simple parameterised constructor form:

  names <- List() of String

This declares a class field 'names' of type 'List of String' and initialises it to an empty list. The List type is always 'set' when created, so you do not need an is-set check on it.

WHY THIS CATCHES PEOPLE

C#, Kotlin, TypeScript, and Swift all use a trailing '?' on a type to mean 'nullable'. EK9 has no null. '?' in EK9 is the is-set operator — it is a unary postfix method that returns a Boolean telling you whether the object it is called on currently holds a meaningful value. Writing 'List() of String?' therefore means 'construct an empty List, then ask whether it is set' — not 'a List field that might be unset'.

FIELD DECLARATION FORMS THAT WORK

  names <- List() of String                 // empty List of String
  scores <- Dict() of (String, Integer)     // empty Dict
  tags <- ["alpha", "beta", "gamma"]         // List literal (type inferred)
  counters as Integer: 0                    // explicit type + initial value
  label as String: "default"                // explicit type + literal
  choice as String: isReady <- "a" : "b"   // explicit type + ternary

FIELD DECLARATION FORMS THAT FAIL WITH E04050

  Holder
    isReady <- true
    names <- List() of String?        // ERROR E04050: '?' invoked on the List
    choice <- isReady <- "a" : "b"    // ERROR E04050: an EK9 ternary - write 'choice as String: isReady <- "a" : "b"'

WHEN IN DOUBT USE EXPLICIT TYPING

If the field's type is at all complex, abandon '<-' and use 'name as Type: value'. The explicit form tells the compiler the type up front and lets full expression processing happen later:

  total as Float: price * quantity    // explicit type survives complex expr

See Q585 for field initialisation and constructor delegation. See Q104 for why EK9 requires explicit constructors on some uninitialised fields.

Example

#!ek9
defines module qa.classesandoop.e04050fieldfix

  defines class

    UniqueNames
      names <- List() of String

      add()
        -> name as String
        if names not contains name
          names += name

      count() as pure
        <- rtn as Integer: length names

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

      operator $ as pure
        <- rtn as String: `UniqueNames(${length names})`

  defines program

    E04050FieldFixDemo()
      stdout <- Stdout()

      names <- UniqueNames()
      names.add("Alice")
      names.add("Bob")
      names.add("Alice")
      stdout.println($names)

Common mistakes

E04050 — The trailing '?' invokes the is-set operator on the constructed List, producing a Boolean. Type inference cannot reconcile that with a List-of-String field. Drop the '?' — EK9 has no nullable types, and the List itself is always set once created.

Incorrect:

      names <- List() of String?

Correct:

      names <- List() of String
Other ways to ask this
  • I'm getting E04050 'not expecting complex expression' on a class field — what's wrong?
  • Why does 'names <- List() of String?' fail to compile in a class body?
  • How do I initialize a List field in an EK9 class without E04050?
  • The compiler says my field declaration needs a simple aggregate — how do I fix it?

Coming from another language?

C#/Kotlin: 'List<string>?' means nullable List — a valid type. EK9: 'List() of String?' means 'call ? on the newly-created List' — produces Boolean, fails type inference. If you want a field that might be unset in EK9, the collection itself is always set (empty List IS valid); the question 'is this field populated' is answered by '?' on the field at the point you check it, not in the type declaration.

Keywords: E04050, List, complex expression, field declaration, is-set operator, nullable type, type inference