Why doesn't EK9 have variable arguments (varargs)?

← Functions and Methods · Ref: Q599

EK9 deliberately excludes variable arguments (varargs). This is a designed exclusion based on the problems varargs create.

WHY VARARGS WERE REMOVED

Variable arguments lose type safety at boundaries. In Java, 'printf(String, Object...)' accepts anything, and type errors become runtime exceptions. Varargs hide the actual parameter count from complexity metrics, allowing functions to accept unbounded input while appearing simple. They also make APIs ambiguous when combined with overloading.

THE EVIDENCE

Java varargs with generics produce heap pollution warnings. C printf-style varargs are a major source of security vulnerabilities (format string attacks). Python *args encourages functions that accept everything and validate nothing.

EK9 ALTERNATIVE: PASS A LIST

Instead of varargs, pass a typed List explicitly:

  sumAll()
    -> numbers as List of Integer
    <- total as Integer: 0
    for number in numbers
      total += number

Call with a list literal:

  result <- sumAll([1, 2, 3, 4, 5])

This approach is typed (the compiler knows the element type), bounded (the list has a known length at the call site), and self-documenting (the parameter name describes the collection).

STREAM ALTERNATIVE

For transforming variable-length data, use stream pipelines:

  items <- ["hello", "world", "foo"]
  cat items > stdout

Pipelines handle collections of any size naturally.

See Q49 for function basics. See Q45 for List type. See Q597 for parameter syntax. See Q598 for why default parameters are also excluded.

Example

defines module qa.functionsAndMethods.noVarargs

  defines function

    //Instead of varargs: accept a typed List
    sumAll()
      -> numbers as List of Integer
      <- total as Integer: 0
      for number in numbers
        total += number

    //Another typed list alternative
    joinAll()
      ->
        items as List of String
        separator as String
      <- result as String: String()
      first <- true
      for item in items
        if first
          result: item
          first: false
        else
          result: `${result}${separator}${item}`

  defines program

    NoVarargsDemo()
      stdout <- Stdout()

      // === PASS A LIST INSTEAD OF VARARGS ===
      total <- sumAll([1, 2, 3, 4, 5])
      stdout.println(`Sum: ${total}`)

      total2 <- sumAll([10, 20])
      stdout.println(`Sum2: ${total2}`)

      // === JOIN WITH SEPARATOR ===
      joined <- joinAll(["hello", "world", "foo"], ", ")
      stdout.println(`Joined: ${joined}`)

      // === LIST LITERAL IS CONCISE ===
      stdout.println(`Empty sum: ${sumAll(List() of Integer)}`)

      // === STREAM PIPELINE FOR VARIABLE DATA ===
      items <- ["alpha", "beta", "gamma"]
      cat items > stdout

Common mistakes

E50060 — Integer has no toString() method in EK9; use the '$' prefix operator or string interpolation instead. See ek9 -h E50060 for details.

Incorrect:

${total.toString()}

Correct:

${total}

E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.

Incorrect:

joined <- joinAll(["hello", "world", "foo"], ", ").toUpperCase()

Correct:

joined <- joinAll(["hello", "world", "foo"], ", ")
Other ways to ask this
  • Can I pass a variable number of arguments to a function in EK9?
  • What replaces varargs in EK9?
  • How do I handle unknown numbers of parameters in EK9?

Coming from another language?

Java: varargs with Object... or specific type, heap pollution with generics, autoboxing complications. Python: *args and **kwargs accept anything, no compile-time type checking. JavaScript: rest parameters (...args) with no type safety, arguments object legacy. Rust: no varargs, macros (println!) handle variable arguments at compile time. Go: variadic functions with ...Type, type-safe but only for last parameter. C: va_list varargs are type-unsafe, source of format string vulnerabilities. Kotlin: vararg keyword, similar to Java. Swift: variadic parameters with Type..., type-safe. EK9: no varargs by design, pass List of T explicitly, typed and bounded, stream pipelines for variable-length processing.

Keywords: function, variable, typed, migrate, parameter, printf, collection, method, arguments, stream, list, bounded, varargs