Core syntax Static typing Argument binding

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.

Positional Variadic Parameters

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
Named Variadic Parameters

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"
Keys are always strings

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.

Combining Both Packs
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.

Declaration and Call Rules
RuleCompiler behavior
Positional pack countAt most one per callable.
Named pack countAt most one per callable.
Pack orderThe positional pack must precede the named pack.
Named pack positionIt must be the final parameter.
Named pack key typeIt must be String; argument names cannot be numeric keys.
Default valueA pack cannot declare a default value; an empty pack is supplied automatically.
Duplicate parameter nameRejected while parsing the declaration.
Duplicate named argumentRejected at the call site during compilation.
Positional after namedRejected at the call site.
Overload Resolution

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"
Methods, Constructors, and Generics

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
Compilation and Runtime Model

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.