Defining and Calling Procedures

Write a procedure, choose how many arguments it accepts, and use apply to pass arguments stored in a list.

When this matters

Write a procedure to reuse a calculation or pass it to another procedure. Use apply when some or all of its arguments are stored in a list.

See it run

Save this complete program as procedures.lspx. Install the Native CLI first, or run each example separately in the Playground.

LISPEX
(define (sum first . rest) (apply + first rest))
(sum 1 2 3 4)
SH
lispex run procedures.lspx

Output

OUTPUT
10

The command exits with status 0 and creates no files.

Read the example

sum requires at least one argument. first receives 1, while rest receives the fresh list (2 3 4). apply combines the explicit first argument with that list and invokes + once.

The names first and rest are parameters. The values supplied by a call are arguments. The number of arguments a procedure accepts is its arity. The dot before rest means that this parameter collects the remaining arguments into a new list.

The expression (apply + first rest) is the procedure's body, the code that runs when it is called. In this example, it has the same effect as (+ 1 2 3 4).

Define names inside a procedure

LISPEX
(define (g)
  (define a 1)
  (define b 2)
  (+ a b))

(g)

Observed result

OUTPUT
3

The definitions of a and b create names local to g. Consecutive define forms at the start of a body are grouped together. Procedures defined in this group can call each other.

Keep these definitions together at the top. A define after another expression is outside that initial group.

Choose parameters and a call form

Use define to name a procedure. Use lambda to create a procedure value without naming it. Both find names according to the surrounding source structure, following the lexical scope rules. A procedure that keeps access to those names is called a closure.

ShapeBest useArity
(define (f x) body)a named reusable procedurefixed
(lambda (x y) body)an anonymous procedure valuefixed
(define (f . xs) body)a named procedure that gathers its argumentszero or more
(lambda (x . rest) body)required arguments plus a remainderat least the fixed prefix
(apply f a final-list)arguments assembled as datachecked by f

The ... in (x ...) and (x ... . rest) means more parameter names, not literal source text. These are the two accepted shapes for a lambda parameter list. A bare name without parentheses, as in (lambda args ...), raises E130.

A rest parameter receives a new proper list, whose final tail is (). apply also requires its final argument to be a proper list. For example, '(1 2) is proper, while '(1 . 2) ends in 2 and is improper.

Check arguments before the body runs

Lispex evaluates the procedure expression first, then the argument expressions from left to right. An argument count mismatch raises E302 before the body runs. For example, (sum) supplies none of the arguments required by sum.

Calling a value that is not a procedure raises E301. An improper final list, such as in (apply + '(1 . 2)), raises E312.

Use apply for the last call

When apply is the final action in a procedure, it preserves tail position. The called procedure's result becomes the caller's result without adding a return step. See tail calls for examples.

Lispex uses the same call handler for direct calls and apply. It executes Lispex closures itself; it does not delegate them to the implementation language's apply.

Keep going

Higher-order procedures explains passing procedures to operations such as map and filter. The closures guide shows how a procedure keeps access to names after the surrounding code returns.

Higher-order procedures · Closures guide