Why doesn't EK9 have default parameter values?

← Functions and Methods · Ref: Q598

EK9 deliberately excludes default parameter values. This is a designed exclusion based on the problems defaults create in production code.

WHY DEFAULTS WERE REMOVED

Default parameter values cause three categories of bugs. First, they hide complexity at call sites: callers cannot tell which defaults are being used without reading the function signature. Second, they create fragile APIs: changing a default value silently changes behaviour for all callers. Third, they complicate overload resolution: when combined with method overloading, defaults create ambiguous call resolution that even compiler writers find difficult to specify correctly.

THE EVIDENCE

C++ default parameters combined with virtual methods create inheritance bugs (base vs derived defaults). Python mutable default arguments (def f(x=[])') are a well-known trap. Kotlin and Scala defaults with overloading create ambiguous resolution requiring explicit disambiguation.

EK9 ALTERNATIVES

Constructor overloading provides the same convenience for classes:

  Config()
    -> maxRetries as Integer
    this(maxRetries, 30)
  Config()
    -> maxRetries as Integer, timeoutSeconds as Integer
    this.maxRetries: maxRetries
    this.timeoutSeconds: timeoutSeconds

Function delegation chains one function to another with explicit values:

  connectSimple()
    -> host as String
    <- rtn as String: connectFull(host, 8080)

Every call site is explicit. Every value is visible. No hidden assumptions.

See Q49 for function basics. See Q94 for constructor overloading. See Q118 for builder pattern as alternative for many optional values. See Q580 for constructor delegation patterns. See Q597 for parameter syntax.

Example

defines module qa.functionsAndMethods.noDefaults

  defines class

    //Constructor overloading replaces default parameters
    Config
      maxRetries <- 0
      timeoutSeconds <- 0

      Config()
        -> maxRetries as Integer
        this(maxRetries, 30)

      Config()
        ->
          maxRetries as Integer
          timeoutSeconds as Integer
        this.maxRetries: maxRetries
        this.timeoutSeconds: timeoutSeconds

      describe() as pure
        <- rtn as String: `retries=${maxRetries}, timeout=${timeoutSeconds}`

      default operator ?

  defines function

    //Function delegation: wraps another function with explicit values
    connectSimple() as pure
      -> host as String
      <- rtn as String: connectFull(host, 8080)

    connectFull() as pure
      ->
        host as String
        port as Integer
      <- rtn as String: `${host}:${port}`

  defines program

    NoDefaultParamsDemo()
      stdout <- Stdout()

      // === CONSTRUCTOR OVERLOADING (replaces default params) ===
      simple <- Config(3)
      stdout.println(`Simple: ${simple.describe()}`)

      full <- Config(5, 60)
      stdout.println(`Full: ${full.describe()}`)

      // === FUNCTION DELEGATION (replaces default params) ===
      stdout.println(connectSimple("localhost"))
      stdout.println(connectFull("localhost", 9090))

Common mistakes

E05050 — The this() delegation call must be the very first statement in the constructor body. No code can execute before delegation. See ek9 -h E08110 for details.

Incorrect:

Config()
        -> maxRetries as Integer
        maxRetries: maxRetries + 1
        this(maxRetries, 30)

Correct:

Config()
        -> maxRetries as Integer
        this(maxRetries, 30)
Other ways to ask this
  • Can I set default values for function parameters in EK9?
  • How do I handle optional parameters without defaults in EK9?
  • What replaces default parameter values in EK9?

Coming from another language?

Java: no default parameters, method overloading used instead, builder pattern for many options. Python: def func(x, timeout=30) supports defaults, mutable default bug is well-known, overuse leads to functions with 10+ parameters. JavaScript: function(x, timeout = 30) with ES6 defaults, no type safety. Rust: no default parameters, builder pattern or Option types used instead. Go: no default parameters, functional options pattern (variadic WithXxx functions). Kotlin: fun connect(host: String, port: Int = 8080) with named defaults, can conflict with overloading. C#: optional parameters with default values, can break binary compatibility. Swift: func connect(host: String, port: Int = 8080) with defaults, named parameters help readability. EK9: no default parameters by design, use constructor overloading or function delegation, every value explicit at call site.

Keywords: safe, migrate, hidden, parameter, delegation, guard, default, method, optional, complexity, overload, constructor, absent, function, explicit, value