Variadic Parameters
Klyn can collect extra positional arguments, extra named arguments, or both. Each pack keeps a concrete static type, participates in overload resolution, and never absorbs arguments intended for the other pack.
The syntax values ... as T accepts zero or more positional values of type
T. Inside the callable, values is an
ArrayList<T>. The compiler validates every supplied element before generating the
call.
def sumAll(values ... as Int) as Int:
total = 0
for value in values:
total += value
return total
assert sumAll() == 0
assert sumAll(10, 20, 30) == 60
The syntax kwargs ... as String:V collects named arguments that do not match a fixed
parameter. The body receives a Map<String,V>. A named argument is identified by
its parameter name, so its key type is always String; each value must be assignable
to V.
import klyn.collections
def describe(kwargs ... as String:Object) as String:
if "name" in kwargs:
return String(kwargs["name"])
return "anonymous"
assert describe() == "anonymous"
assert describe(name="Ada", active=true) == "Ada"
The key type remains explicit for readability, but it must be String. A declaration
such as kwargs ... as Int:Object is rejected during compilation. The value type may
be any valid Klyn type, including a generic type parameter.
import klyn.collections
def configure(
first as String,
values ... as Int,
kwargs ... as String:Object
) as Void:
print(first)
print(values)
print(kwargs)
configure("build", 10, 20, profile="release", trace=false)
In this call, 10 and 20 enter values while
profile and trace enter kwargs. Positional arguments never
enter the named pack, and named arguments never enter the positional pack.
| Rule | Compiler behavior |
|---|---|
| Positional pack count | At most one per callable. |
| Named pack count | At most one per callable. |
| Pack order | The positional pack must precede the named pack. |
| Named pack position | It must be the final parameter. |
| Named pack key type | It must be String; argument names cannot be numeric keys. |
| Default value | A pack cannot declare a default value; an empty pack is supplied automatically. |
| Duplicate parameter name | Rejected while parsing the declaration. |
| Duplicate named argument | Rejected at the call site during compilation. |
| Positional after named | Rejected at the call site. |
A named pack is a fallback, not a competitor to an exact parameter. When a named argument matches a fixed parameter, the more precise overload wins.
def choose(value as Int) as String:
return "exact"
def choose(kwargs ... as String:Object) as String:
return "fallback"
assert choose(value=10) == "exact"
assert choose(other=10) == "fallback"
assert choose() == "fallback"
The same syntax and binding rules apply to top-level functions, instance or static methods, constructors, delegated constructors, and generic callables. Generic inference uses the pack element or value types rather than the implementation collection itself.
import klyn.collections
class Options:
public property values as Map<String,Object>
public Options(kwargs ... as String:Object):
this.values = kwargs
def last<T>(first as T, values ... as T) as T:
return first if values.size == 0 else values[values.size - 1]
options = Options(theme="dark", retries=3)
assert last(10, 20, 30) == 30
Packs are fully typed during compilation. Klyn lowers them directly to concrete collection instances required by the selected signature; it does not perform a dynamic keyword lookup to discover an overload at runtime. Generic packs are specialized like other generic parameters.