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