Status: implemented
- Fixed names bind positional arguments; missing arguments are
undefined. Extra arguments are permitted. The rest parameter is the array of arguments after the fixed prefix. Each invocation has its own rest array, and repeated reads within it return the same array. Parameters may shadow outer names, but must be distinct and cannot collide with body declarations. Each one, fixed or rest, is a name by the binding rule:(eval) => 1and(...let) => 1are refused, while(from, then) => [from, then]is a function. - A default,
= valueafter a fixed parameter, a name or a pattern, is the parameter's value where its argument isundefined— missing, or passed asundefined— and the argument where it is anything else,nullincluded. The default is evaluated only where it is taken, at each call:(a = {}) => aanswers a fresh object at each call, and(a = null.x) => athrows only when called withouta. Where it is taken, it is evaluated at the call, before the body, whether or not the body goes on to read the parameter:(a, b = null.x) => a === 5 ? b : athrows when called withoutb,f(6)included, as JavaScript's does, the parameter being an entry of the body as aconstis. It is resolved in the function's own scope, before the body: it reads the scopes around the function, through the function's frame as the body does, the function itself, and the parameters before it,(a, b = a) => b. A read of a later parameter or of the parameter itself, which JavaScript throws on when the default is taken, is refused rather than read from a scope around the function:const not foundwhere no scope around binds the name, andcapture shadowedat the parameter where one does, a read inside a function the default writes included. A bodyconstthat shadows a name a default read is refused the same way,capture shadowed, as one that shadows a name the body read is. The parameters from the first default on, and the rest parameter after them, are read from the rest array, so the graph needs no node of its own:(a = 5) => ais(...r) => r[0] === undefined ? 5 : r[0], the outputs writing that lowered form (default parameters). f.lengthis the number of fixed parameters, including unused ones and counting a pattern as one, before the first parameter with a default, as JavaScript's is:((a, b = 1, c) => 0).lengthis1. A default inside a destructuring pattern does not end the count:(({ a = 5 }) => a).lengthis1, while a default on the whole parameter,(({ a = 5 } = {}) => a).length, is0. The first form still throws for an omitted ornullargument; the second permits an omitted argument and still throws fornull(destructuring). Rest adds zero. A function has at most 16 fixed parameters, every one written counted, defaulted or not: a 17th is a compile error, and an EDAG function whoselengthis above 16 is refused by every writer. Wider data reads better as an array or an object, and a rest parameter still takes any number of arguments. The EDAG interpreters retain the function's length, body and evaluated captures as represented values; conversion to ordinary runtime callables happens at an explicit target compile/load boundary (EDAG values).- An empty parameter list binds no name at all, so a body written under one cannot reach its arguments: the arguments array is named by the parameter and by nothing else, and a word the list does not spell is unbound here exactly as any other unbound word is. Nothing else distinguishes the two lists.
() => 1and(...args) => 1denote the one function, the one node['=>', 0, [], 1]— though each arrow written is a function of its own (below) — and a bodyconstmay take the name a parameter would have taken, there being no parameter to collide with.
The compiler demo offers the programs parameters in module.f.js, each with its real output.
The demo on this page runs sample.f.js, this page's example as a function of one number — a fixed parameter, a default and a rest parameter — with every output fjs compile makes of it: the expression graph (EDAG), the Rust and the WebAssembly that Rust builds to. Its Run button calls the default export with 1 and 4, as JavaScript and as WebAssembly, and shows what each call returned or threw.
const f = (x, y = 10, ...rest) => [x, y, rest.length];
export default (...a) => [f(a[0]), f(a[0], a[0] * 2, 7, 8)];