The defun Special Form
In Common LISP, you define a named function with the defun special form: (defun name (parameters) body). The body is a sequence of expressions, and the value of the last expression evaluated is automatically returned — there is no explicit return keyword needed in the common case. For example, (defun square (x) (* x x)) creates a function named square that takes one parameter and returns its value multiplied by itself.
Cricket analogy: Just as a scorer registers a bowler's name once in the scorebook before recording every over they bowl, defun registers a function name once so every later call like (square 5) can reuse that definition, the way Jasprit Bumrah's figures accumulate under one entry across a spell.
(defun square (x)
"Return the square of X."
(* x x))
(square 5) ; => 25
(defun greet (name &optional (greeting "Hello"))
(format nil "~A, ~A!" greeting name))
(greet "Ada") ; => "Hello, Ada!"
(greet "Ada" "Welcome") ; => "Welcome, Ada!"Parameter Lists: Required, Optional, and Keyword Arguments
LISP's lambda lists go well beyond simple positional parameters. After the required parameters, you can add &optional parameters with default values, &rest to collect any remaining arguments into a list, and &key to accept named keyword arguments in any order. In (defun greet (name &optional (greeting "Hello")) ...), greeting defaults to "Hello" if the caller omits it, but the caller can still supply a second positional argument to override it.
Cricket analogy: This is like a team sheet where the top-order batters are mandatory (required parameters) but the wicketkeeper slot has a default choice, say MS Dhoni, unless the captain names someone else — the &optional slot has a fallback but can be overridden.
Common LISP functions are automatically documented when you include a string literal as the first form in the body — this becomes the function's docstring, retrievable at the REPL with (documentation 'square 'function).
Implicit Return and Multiple Values
Unlike languages that require an explicit return statement, a LISP function's return value is simply the value of the last form evaluated in its body, following normal evaluation order top to bottom. LISP functions can also return multiple values using (values a b c), which callers can capture with multiple-value-bind — this is distinct from returning a list, and is commonly used by functions like truncate that need to hand back both a quotient and a remainder without allocating a cons cell.
Cricket analogy: The implicit return is like the final ball of an over automatically deciding the total runs for that over — you don't need a separate 'announce total' step, it's just whatever the last delivery's outcome contributes, while multiple-value-bind is like a scorer capturing both 'runs' and 'wickets' from one over simultaneously.
Forgetting that the last expression's value is returned is a common beginner mistake — if you add a debugging (format t "...") call as the last line of a function body, the function will return the value of format (typically nil) instead of the value you intended, silently breaking callers.
- defun defines a named function: (defun name (params) body).
- The value of the last evaluated expression in the body is returned automatically — no explicit return needed.
- A leading string literal in the body becomes the function's docstring.
- &optional parameters can specify default values; &rest collects extra arguments into a list; &key allows named arguments in any order.
- Functions can return multiple values via (values ...), captured with multiple-value-bind, distinct from returning a list.
- Adding trailing debug statements can accidentally change a function's return value — watch the last form in the body.
Practice what you learned
1. What does (defun square (x) (* x x)) return when called as (square 4)?
2. How do you give a function a docstring in Common LISP?
3. In (defun greet (name &optional (greeting "Hello")) ...), what happens if greet is called with only one argument?
4. What is the primary purpose of &rest in a lambda list?
5. How do you return more than one value from a Common LISP function?
Was this page helpful?
You May Also Like
Lambda and Anonymous Functions
Learn how to create unnamed functions with lambda in LISP, and how they're used with higher-order functions like mapcar, remove-if, and reduce.
let and Local Bindings
Learn how let and let* create local variable bindings in LISP, and how their scoping rules differ from global assignment.
Recursion in LISP
Understand how recursion works in LISP, including base cases, recursive list processing, and tail-call optimization.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics