Variadic functions

Up till now all functions have had a specified number of named arguments. We will now introduce a syntax for defining variadic functions, which may take a fixed number of named arguments and a variable number of additional arguments which are collected into a named list.

The argument declarations for variadic functions are improper lists:

λ-syntaxCombined DEFINE
3 args (LAMBDA (arg1 arg2 arg3) body...) (DEFINE (name arg1 arg2 arg3) body...)
≥2 args (LAMBDA (arg1 arg2 . rest) body...) (DEFINE (name arg1 arg2 . rest) body...)
≥1 args (LAMBDA (arg1 . rest) body...) (DEFINE (name arg1 . rest) body...)
≥0 args (LAMBDA args body...) (DEFINE (name . args) body...)

In the definitions above, the parameters are bound as follows:

Definition (f 1 2 3)
Value of a Value of b Value of c
(DEFINE (f a b c) body...) 1 2 3
(DEFINE (f a b . c) body...) 1 2 (3)
(DEFINE (f a . b) body...) 1 (2 3)
(DEFINE (f . a) body...) (1 2 3)

Implementation

All that is required is a small modification to make_closure to accept the declaration:

int make_closure(Atom env, Atom args, Atom body, Atom *result)
{
	Atom p;

	if (!listp(body))
		return Error_Syntax;

	/* Check argument names are all symbols */
	p = args;
	while (!nilp(p)) {
		if (p.type == AtomType_Symbol)
			break;
		else if (p.type != AtomType_Pair
				|| car(p).type != AtomType_Symbol)
			return Error_Type;
		p = cdr(p);
	}

	*result = cons(env, cons(args, body));
	result->type = AtomType_Closure;

	return Error_OK;
}

And another to apply to bind the additional arguments into a list:

int apply(Atom fn, Atom args, Atom *result)
{
	.
	.
	.
	/* Bind the arguments */
	while (!nilp(arg_names)) {
		if (arg_names.type == AtomType_Symbol) {
			env_set(env, arg_names, args);
			args = nil;
			break;
		}

		if (nilp(args))
			return Error_Args;
		env_set(env, car(arg_names), car(args));
		arg_names = cdr(arg_names);
		args = cdr(args);
	}
	if (!nilp(args))
		return Error_Args;
	.
	.
	.
}

Testing

A boring example:

> ((lambda (a . b) a) 1 2 3)
1
> ((lambda (a . b) b) 1 2 3)
(2 3)
> ((lambda args args) 1 2 3)
(1 2 3)

We can also create a variadic adder:

> (define (sum-list xs)
    (if xs
        (+ (car xs) (sum-list (cdr xs)))
        0))
SUM-LIST
> (sum-list '(1 2 3))
6
> (define (add . xs) (sum-list xs))
ADD
> (add 1 2 3)
6
> (add 1 (- 4 2) (/ 9 3))
6

Since you can always pass a list to a regular function, this is really just another kind of syntactic sugar.