Skip to content

LISP - Basic Syntax

Welcome to the world of Lisp! This tutorial focuses on Common Lisp, a powerful, standardized, and multi-paradigm programming language. We’ll start with the foundational syntax and guide you through setting up a modern development environment.

For a productive Lisp experience, an interactive setup is key. Here’s a recommended modern toolchain:

  • Implementation: SBCL (Steel Bank Common Lisp) is a high-performance, open-source compiler. It’s a great choice for both beginners and professional development.
  • Package Manager: Quicklisp is the de-facto package manager for Common Lisp. It allows you to download and manage hundreds of open-source libraries with simple commands.
  • Build System: ASDF (Another System Definition Facility) is the standard tool for defining how Lisp source files are compiled and loaded as a cohesive system (project).
  • Editor: An editor with strong Lisp integration is crucial. Emacs with SLIME or Sly provides a deeply interactive REPL-driven experience. Alternatively, VS Code with extensions like Alive or SLIMA offers a modern alternative.

Lisp programs are built from S-expressions (Symbolic Expressions), which come in two forms:

  • Atoms: An atom is anything that is not a list. This includes numbers, symbols (names for variables and functions), and strings.
  • Lists: A list is a sequence of atoms and/or other lists, enclosed in parentheses ().
123 ; A number
3.14159 ; A floating-point number
'my-symbol ; A symbol (often used for variables or functions)
:my-keyword ; A keyword (a self-evaluating symbol)
"Hello, Lisp!" ; A string
(+ 1 2) ; A list representing a function call
'(a b c) ; A list of symbols
(list 1 "two" '(three)) ; A nested list
() ; The empty list, also known as NIL

Lisp operates on a Read-Eval-Print Loop (REPL). It reads an S-expression, evaluates it, and prints the result. The key rule of evaluation is:

  • Symbols are treated as variables and evaluated to their values.
  • Lists are treated as function calls, where the first element is the function name and the rest are its arguments.

What if you want to treat a list as just data? You quote it.

The single quote mark ' is a shorthand for the quote special operator. It tells Lisp to inhibit evaluation and treat the following expression as literal data.

; Define a variable 'a'
(defvar a 10)
; This is a list of two symbols, 'a' and 'b'.
; Lisp tries to find the function named 'a', which fails.
;;; (a b) -> ERROR: The function A is undefined.
; This is a list of data, not a function call.
; The expression returns the list itself.
(print '(a b))
; The function `*` is called with arguments 2 and 3.
(print (* 2 3))
; The list `(* 2 3)` is treated as data and not evaluated.
(print '(* 2 3))
(A B)
6
(* 2 3)

Semicolons (;) are used for comments. A single semicolon is typically for comments on the same line as code. Multiple semicolons are used for block-level comments by convention.

;; This function greets the user.
(defun greet (name)
(format t "Hello, ~a!~%" name)) ; Print a formatted string to the console
  • Function Calls: Lisp uses prefix notation: (function-name arg1 arg2). For example, (+ 1 2 3) sums the numbers.
  • Case Sensitivity: By default, the Lisp reader converts un-escaped symbol names to uppercase. So, my-function and MY-FUNCTION refer to the same symbol. The modern convention is to type in lowercase and let the reader case-fold it.
  • Truth and Falsehood: In Common Lisp, the symbol T represents the boolean true. The symbol NIL represents boolean false. NIL is uniquely also the representation for the empty list ().
  • Special Forms: Some expressions that look like function calls are actually special forms or macros. They have their own evaluation rules and are essential for control flow (if, when), variable binding (let), and defining things (defun, defvar).

The idiomatic style in Common Lisp is to use kebab-case for symbol names, separating words with hyphens (-). For example: my-cool-function, *global-variable* (with earmuffs for special/global variables).