Rest parameters allow functions to accept a variable number of arguments, collected into an array.
Two syntaxes are supported:
;; JS-style: ...name
(fn sum [...nums]
(.reduce nums (fn [acc val] (+ acc val)) 0))
;; Clojure-style: & name
(fn sum [& nums]
(.reduce nums (fn [acc val] (+ acc val)) 0))
Both compile to JavaScript ...rest parameters.
(fn sum [...nums]
(.reduce nums (fn [acc val] (+ acc val)) 0))
(sum 1 2 3 4 5) ;; => 15
(fn sum [x ...rest]
(+ x (.reduce rest (fn [acc val] (+ acc val)) 0)))
(sum 10 1 2 3) ;; => 16
(fn sum [x y ...rest]
(+ x y (.reduce rest (fn [acc val] (+ acc val)) 0)))
(sum 10 20 1 2 3) ;; => 36
When no extra arguments are passed, the rest parameter is an empty array:
(fn getLength [...items]
(get items "length"))
(getLength) ;; => 0
(getLength 1 2) ;; => 2
Rest parameters can follow parameters that have defaults. Rest parameters themselves cannot have defaults.
(fn process [x = 5 ...rest]
(+ x (.reduce rest (fn [acc val] (+ acc val)) 0)))
(process 10 1 2 3) ;; => 16
(process _ 1 2 3) ;; => 11 (uses default 5 for x)
Destructuring patterns can appear before a rest parameter:
;; Array destructuring + rest
(fn process [[a b] ...rest]
(+ a b (.reduce rest (fn [acc x] (+ acc x)) 0)))
(process [5 10] 1 2 3) ;; => 21
;; Object destructuring + rest
(fn process [{"x": x} ...rest]
(+ x (.reduce rest (fn [acc val] (+ acc val)) 0)))
(process {"x": 10} 1 2 3) ;; => 16
Arrow functions support rest parameters via explicit parameter lists:
(let sum (=> (...nums)
(.reduce nums (fn [acc x] (+ acc x)) 0)))
(sum 1 2 3 4) ;; => 10
(let multiply (=> (factor ...nums)
(.map nums (fn [x] (* factor x)))))
(multiply 3 1 2 3) ;; => [3, 6, 9]
In multi-arity functions, a rest-parameter arity acts as a catch-all for argument counts not matched by fixed arities:
(fn calculate
([base = 100 multiplier = 2 & rest]
(+ (* base multiplier) (.reduce rest (fn [acc x] (+ acc x)) 0))))
Values can be spread into functions that accept rest parameters:
(fn sum [...nums]
(.reduce nums (fn [acc x] (+ acc x)) 0))
(fn average [...values]
(/ (sum ...values) (get values "length")))
(average 10 20 30) ;; => 20
Rest parameters are real JavaScript arrays:
;; Indexing
(fn getSecond [...items]
(get items 1))
(getSecond 10 20 30) ;; => 20
;; Length
(fn count [...items]
(get items "length"))
(count 1 2 3 4 5) ;; => 5
;; Array methods (.map, .filter, .reduce, .join, etc.)
(fn doubleAll [...nums]
(.map nums (fn [n] (* n 2))))
(doubleAll 1 2 3) ;; => [2, 4, 6]
;; HQL
(fn sum [...nums]
(.reduce nums (fn [a b] (+ a b)) 0))
;; Compiles to JavaScript
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
;; Valid
(fn f [...rest])
(fn f [a ...rest])
(fn f [a b ...rest])
(fn f [a = 1 ...rest])
(fn f [[x y] ...rest])
;; Invalid (parser stops at first rest; anything after is ignored)
;; (fn f [...rest a])
;; (fn f [...a ...b])
breaks after the first rest indicator (tokens after it are silently ignored). In single-arity parseParameters, there is no break -- extra tokens after rest are processed as additional parameters (likely producing invalid output).src/hql/transpiler/syntax/function.ts (parseParameters, transformMultiArityFn)tests/unit/syntax-rest-params.test.ts, tests/unit/organized/syntax/function/function.test.ts