This skill should be used when the user asks to "write common lisp", "CLOS", "ASDF", "defpackage", "defsystem", or works with Common Lisp, SBCL, or Coalton. Provides comprehensive Common Lisp ecosystem patterns and best practices.
Instalación
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
This skill should be used when the user asks to "write common lisp", "CLOS", "ASDF", "defpackage", "defsystem", or works with Common Lisp, SBCL, or Coalton. Provides comprehensive Common Lisp ecosystem patterns and best practices.
version
2.1.0
Provide comprehensive patterns for Common Lisp, CLOS, ASDF system definition, SBCL-specific features, and Coalton integration.
Read - Analyze ASDF system definitions and Lisp source files
Edit - Modify Common Lisp code and system definitions
Bash - Run SBCL, Roswell, Qlot commands
mcp__plugin_claude-code-home-manager_context7__query-docs - Fetch ASDF, SBCL, and library documentation
Homoiconic syntax: code and data share the same structure, enabling powerful macro systems
Generic functions with multiple dispatch; method combination (:before, :after, :around)
Handler-case for catching, restart-case for recovery points; more powerful than exceptions
Namespace management with defpackage; use :import-from or local-nicknames over bare :use
<common_lisp_fundamentals>
Code and data share the same syntax (homoiconicity). Enables powerful macro systems for code transformation.
First-class named objects used for identifiers. Interned in packages, can have value, function, and property list.
Functions can return multiple values using values, multiple-value-bind, multiple-value-list.
Special variables with dynamic scope using defvar/defparameter. Convention: *earmuffs* for special variables.
Common Lisp Object System - Generic functions and multiple dispatch
Define a class with slots. Slot options: :initarg, :initform, :accessor, :reader, :writer, :type, :documentation.
(defclass person ()
((name :initarg :name :accessor person-name)
(age :initarg :age :accessor person-age))
(:documentation "Represents a person."))
Define generic functions with multiple method implementations.
(defgeneric greet (entity)
(:documentation "Greet an entity."))
(defmethod greet ((p person))
(format t "Hello, ~a!~%" (person-name p)))
</example>
<decision_tree name="when_to_use">
<question>Do you need polymorphic behavior based on multiple types?</question>
<if_yes>Use defgeneric and defmethod for multiple dispatch</if_yes>
<if_no>Use regular functions for single implementation</if_no>
</decision_tree>
Method qualifiers (:before, :after, :around) for aspect-oriented programming.
(defmethod greet :before ((p person))
(format t "Preparing to greet...~%"))
(defmethod greet :around ((p person))
(format t "[Start]~%")
(call-next-method)
(format t "[End]~%"))
</example>
Classes can inherit from multiple parent classes. Uses C3 linearization for method resolution order.
(defclass employee (person job-holder)
((employee-id :initarg :id :accessor employee-id)))
Common Lisp condition system - Restarts and handlers
Handle conditions similar to try-catch
(handler-case
(/ 1 0)
(division-by-zero (c)
(format t "Caught: ~a~%" c)
0))
Handle conditions without unwinding stack
(handler-bind
((error #'(lambda (c)
(format t "Error occurred: ~a~%" c)
(invoke-restart 'use-value 0))))
(restart-case
(error "Something went wrong")
(use-value (v) v)))
Define recovery points
(defun parse-entry (entry)
(restart-case
(parse-integer entry)
(use-value (v)
:report "Use a different value"
:interactive (lambda () (list (read)))
v)
(skip-entry ()
:report "Skip this entry"
nil)))
Can the error be recovered interactively or programmatically?
Provide restarts for different recovery strategies
Use handler-case for simple error handling
Define custom condition types for structured error handling.
(define-condition invalid-input (error)
((value :initarg :value :reader invalid-input-value))
(:report (lambda (c stream)
(format stream "Invalid input: ~a"
(invalid-input-value c)))))
Define packages with explicit dependencies and exports.
(defpackage #:my-project
(:use #:cl)
(:import-from #:alexandria #:when-let #:if-let)
(:export #:main
#:process-data))
Define local package nicknames for shorter, clearer references.
(defpackage #:my-project
(:use #:cl)
(:local-nicknames (#:a #:alexandria)
(#:s #:serapeum)))
Another System Definition Facility - Build system for Common Lisp (ASDF 3.4+)
Basic ASDF system definition with metadata and component dependencies.
(defsystem "my-project"
:description "My project description"
:version "0.1.0"
:author "Author Name"
:license "MIT"
:depends-on ("alexandria" "cl-ppcre")
:components ((:file "package")
(:file "utils" :depends-on ("package"))
(:file "main" :depends-on ("utils"))))
Organize system components into modules for better structure.
(defsystem "my-project"
:components
((:module "src"
:components ((:file "package")
(:file "core" :depends-on ("package"))))
(:module "tests"
:depends-on ("src")
:components ((:file "test-suite")))))
Infer dependencies from defpackage forms for modern, maintainable systems.
(defsystem "my-project"
:class :package-inferred-system
:depends-on ("my-project/main"))
;; In my-project/main.lisp:
(defpackage #:my-project/main
(:use #:cl)
(:import-from #:my-project/utils #:helper))
</example>
<decision_tree name="when_to_use">
<question>Do you want automatic dependency inference from package definitions?</question>
<if_yes>Use package-inferred-system for modern projects</if_yes>
<if_no>Use traditional defsystem with explicit component dependencies</if_no>
</decision_tree>
Define test system with automatic test execution using test-op.
(defsystem "my-project/test"
:depends-on ("my-project" "fiveam")
:components ((:file "tests"))
:perform (test-op (o s)
(uiop:symbol-call :fiveam '#:run!
(uiop:find-symbol* '#:my-test-suite :my-project/test))))
Recommended directory layout for Common Lisp projects.
my-project/
├── my-project.asd
├── src/
│ ├── package.lisp
│ ├── utils.lisp
│ └── main.lisp
└── tests/
└── test-suite.lisp
<asdf_path_resolution>
Resolving repository-relative files (fixtures, READMEs, data, sibling test fragments)
correctly under both fresh-process ASDF loads and direct source loads. The core hazard:
when ASDF loads a compiled FASL, load-truename points into the FASL output cache, not the
source tree, so merge-pathnames against it resolves under the cache and fails.
Resolve project-local paths through the system object — asdf:system-relative-pathname or asdf:system-source-directory — not from *load-truename* / merge-pathnames. In a fresh test process *load-truename* may even be unbound inside a test file.
;; robust: anchored to the system's source directory
(asdf:system-relative-pathname :my-project "tests/fixtures/data.txt")
;; fragile under FASL loads: *load-truename* points into the cache
;; (merge-pathnames "fixtures/data.txt" *load-truename*)
</example>
<how_to_apply>
Require :asdf at compile/load/execute time; resolve the base directory from the system
when it is registered; fall back to *compile-file-truename* / *load-truename* /
*load-pathname* only for direct script/source loads that run outside ASDF. This applies to
any split test loader that calls load on sibling fragments.
</how_to_apply>
A fresh or inherited ASDF session must have its source registry pointed at the project root before asdf:load-system; loading the .asd file alone is not sufficient and can stall inside find-system/load-system discovery. Treat clean CL_SOURCE_REGISTRY execution as a required smoke path, run from a child process.
<asdf_system_definition_pitfalls>
Recurring traps when defining a library system plus its test system in a .asd file.
Guarding the test-system definition with (unless (asdf:find-system "proj/test" nil) ...) makes asdf:test-system recurse into the same .asd load path and can surface as a circular dependency during system discovery.
Define the library system and the test system unconditionally; let ASDF handle repeated loads/redefinitions of the .asd file.
Writing :perform (test-op ...) or :in-order-to with a bare test-op resolves to COMMON-LISP-USER::TEST-OP, which is not the ASDF operation class, and fails with class-not-found at run time.
Qualify the operation as asdf:test-op in :perform, and prefer an explicit (asdf:test-system ...) call in the :perform body over a chained :in-order-to graph, which is easier to isolate and less likely to stall the compile/load plan.
:file "src/..." / :file "t/..." relative component paths can raise "Invalid relative pathname" in a raw checkout.
Group components under (:module "src" :pathname "src" :components (...)) so the module carries the pathname, rather than embedding directory segments in each :file.
Defining the canonical test system inside an alias-named .asd (e.g. proj-test.asd), so that loading the library does not let ASDF discover it, triggers an ASDF warning and a fresh-registry smoke gap.
Keep the canonical proj/test system in the primary proj.asd; let the alias-named .asd define only a thin compatibility alias depending on proj/test. In a fresh registry, load the alias system explicitly before asserting the canonical one is reachable.
;; proj.asd — both systems defined unconditionally; module carries the pathname;
;; the operation class is qualified as asdf:test-op and runs the framework directly.
(defsystem "proj"
:components ((:module "src" :pathname "src"
:components ((:file "package")
(:file "core" :depends-on ("package"))))))
<asdf_parallel_execution>
Concurrent CLI/test invocations that each call asdf:load-system can race on an inherited default FASL cache and fail with "Failed to find the TRUENAME of ...fasl". Initialize output translations in the launcher, before load-system, to a private per-user cache, and keep that initialization in the packaged launcher (not only in ad hoc scripts) so every subcommand inherits it.
(asdf:initialize-output-translations
'(:output-translations
(t (:home ".cache" "common-lisp" :implementation))
:ignore-inherited-configuration))
</asdf_parallel_execution>
<constant_reload_safety>
ANSI leaves the consequences undefined if a constant is redefined to a value that is not eql to its current value; SBCL enforces this by signalling SB-EXT:DEFCONSTANT-UNEQL. Because eql is identity-based for compound objects, re-loading a file that defconstant's a vector, list, or string literal fails even when the contents are visually identical, since each load builds a fresh object.
<how_to_apply>Reserve defconstant for scalars and objects with stable eql identity. For tables, vectors, quoted lists, string defaults, and any compound literal that must survive repeated load/compile cycles, use defparameter (or defvar). alexandria:define-constant with :test #'equal is the portable alternative when a genuine constant is required.</how_to_apply>
The eql redefinition rule is ANSI; the DEFCONSTANT-UNEQL condition name is SBCL-specific.
;; unsafe on reload: each load builds a fresh vector, not eql to the prior one
(defconstant +md5-table+ #(1 2 3 4)) ; => SB-EXT:DEFCONSTANT-UNEQL on reload
;; reload-safe: mutable-binding forms rebind without an eql check
(defparameter +md5-table+ #(1 2 3 4))
;; genuine constant with structural identity: alexandria:define-constant
(alexandria:define-constant +md5-table+ #(1 2 3 4) :test #'equalp)
</example>
<test_suite_architecture>
Design principles for organizing a test system so it stays fast, isolatable, and
robust against the compile-unit stalls documented in sbcl-usage.
Keep the main system's runtime dependencies at zero (or minimal) and concentrate test-only dependencies (e.g. FiveAM) in the separate proj/test system. Runtime source then loads in a plain SBCL image, while the canonical verification path is the one that pulls the test framework — commonly a pinned dev shell where the framework is provisioned.
Stratify the test system into explicit tiers — unit, integration, e2e, perf — as separate components, and keep property-based test support in its own support file. This lets a fast tier run in isolation and keeps slow/perf tiers opt-in.
For a component that both defines a surface syntax and executes it, separate the specification/description layer, the parsing layer, and the orchestration layer into distinct units. Beyond clarity, this bounds each compile unit and lets every layer be loaded and tested independently.
Steel Bank Common Lisp - High-performance implementation (current: SBCL 2.6.x, monthly releases)
Create standalone executable with SBCL.
(defun main ()
(format t "Hello, World!~%")
(sb-ext:exit :code 0))
(sb-ext:save-lisp-and-die "my-app"
:toplevel #'main
:executable t
:compression t)
</example>
SBCL threading support with make-thread and mutex synchronization.
(defvar *result* nil)
Call C functions from SBCL using sb-alien interface.
(sb-alien:define-alien-routine "strlen" sb-alien:int
(str sb-alien:c-string))
(strlen "hello") ; => 5
</example>
Use declarations for type information and optimization settings. Options: type, ftype, inline, optimize.
(defun fast-add (x y)
(declare (type fixnum x y)
(optimize (speed 3) (safety 0)))
(the fixnum (+ x y)))
SBCL-specific extensions for system interaction and performance tuning.
;; Command-line arguments
sb-ext:*posix-argv*
Statically typed functional programming on Common Lisp
Define algebraic data types in Coalton with type-safe operations.
(coalton-toplevel
(define-type (Maybe a)
None
(Some a))
(declare safe-div (Integer -> Integer -> (Maybe Integer)))
(define (safe-div x y)
(if (== y 0)
None
(Some (/ x y)))))
</example>
Define type classes for polymorphic behavior in Coalton.
(coalton-toplevel
(define-class (Printable a)
(print-it (a -> String)))
Coalton compiles to efficient Common Lisp code and is interoperable with regular CL.
Use coalton-toplevel for type-safe code sections
Coalton functions can call CL functions and vice versa
Provides Hindley-Milner type inference with type classes
Loop macro for iteration with collection, filtering, and accumulation.
(loop for item in list
for i from 0
when (evenp i)
collect item into evens
finally (return evens))
Common format directives: ~a (aesthetic), ~s (standard), ~d (decimal), ~f (float), ~% (newline), ~{~} (iteration), ~[~] (conditional).
(format t "~a is ~d years old~%" name age)
Document functions with docstrings explaining purpose and parameters.
(defun my-function (arg)
"Docstring describing the function.
ARG is the argument description."
(process arg))
<standard_libraries>
Conservative utility library. Provides essential utilities: when-let, if-let, hash-table-alist, ensure-list, mappings, and more. De facto standard for CL projects.
Comprehensive utility library (superset of alexandria). Provides additional utilities: string manipulation, sequences, types, binding macros, and more.
Common Foreign Function Interface. Portable FFI for calling C libraries from Common Lisp. Preferred over implementation-specific FFI (e.g., sb-alien).
(cffi:defcfun ("strlen" c-strlen) :int
(str :string))
(c-strlen "hello") ; => 5
</example>
<package_sources>
Primary package distribution for Common Lisp. Monthly dist updates with tested library versions.
Complementary distribution with more frequent updates. Tracks latest library versions from GitHub.
<modern_tooling>
Per-project dependency manager (like bundler/npm). Manages dependencies via qlfile, supports Quicklisp and Ultralisp distributions.
<use_case>Install dependencies from qlfile</use_case>
<use_case>Run commands with project dependencies</use_case>
qlot install
qlot exec ros run
Lisp implementation manager and script runner
Install Lisp implementations or libraries
Start REPL with specified implementation
Build standalone executable
ros install sbcl
ros run
ros build myapp.ros
<context7_integration>
Available Context7 documentation libraries for Common Lisp ecosystem.
Common Lisp Docs - General Common Lisp documentation
/lisp-docs/lisp-docs.github.io
4.7
580
ASDF - Another System Definition Facility documentation
/websites/asdf_common-lisp_dev
7.5
190
SBCL - Steel Bank Common Lisp documentation
/sbcl/sbcl
8.0
86
CFFI - Common Foreign Function Interface documentation
/websites/cffi_common-lisp_dev
7.5
198
FiveAM - Testing framework documentation
/websites/fiveam_common-lisp_dev
7.5
164
Coalton - Statically typed functional programming documentation
/coalton-lang/coalton
6.6
568
Use resolve-library-id then get-library-docs for latest documentation.
;; Get ASDF documentation
mcp__plugin_claude-code-home-manager_context7__query-docs
context7CompatibleLibraryID="/websites/asdf_common-lisp_dev"
topic="defsystem"
<best_practices>
Use *earmuffs* for special variables
Use +plus-signs+ for constants
Prefer functional style, minimize mutation
Provide restarts for recoverable situations
Document exported symbols
Use appropriate condition types, not just error
Use check-type for argument validation
Prefer ASDF package-inferred-system for new projects
Consider Qlot for per-project dependency management
Use Roswell for portable script execution
Use Alexandria and Serapeum as standard utility libraries
Use CFFI for foreign function calls (portable across implementations)
Consider Coalton for type-safe functional subsystems
Resolve project-relative paths via asdf:system-relative-pathname / system-source-directory, never load-truename under FASL loads
Define library and test systems unconditionally, and qualify operation classes as asdf:test-op in :perform
Use defparameter/defvar (or alexandria:define-constant :test #'equal) for compound-literal tables; reserve defconstant for eql-stable scalars
Initialize asdf output-translations to a private cache in launchers that may run concurrently
</best_practices>
<anti_patterns>
Global mutable state makes code harder to test and reason about.
Pass state explicitly or use closures to encapsulate mutable state.
Using :use for packages other than :cl creates namespace pollution.
Use :import-from or package-local-nicknames for clearer dependencies.
Ignoring conditions loses error context and recovery opportunities.
Handle conditions with handler-case or handler-bind, and provide appropriate restarts.
Deeply nested code reduces readability and maintainability.
Extract helper functions and use early returns to reduce nesting depth.
Using eval in application code is slow and defeats compile-time optimization.
Use macros for compile-time code generation or first-class functions for runtime dispatch.
Custom reader macros make code harder to read for others.
Use reader macros sparingly and document them clearly when necessary.
Using the loop macro for all iteration, even when simpler constructs suffice.
Use mapcar/remove-if/reduce for simple functional transforms. Consider iterate or series for complex iteration that loop handles poorly. Reserve loop for multi-clause iteration with collection and accumulation.
Using simple error signaling without restarts, or catching and discarding conditions.
Design APIs with restart-case to offer recovery strategies. Use handler-bind to handle conditions without unwinding the stack when possible.
Using defconstant for vectors, lists, strings, or other compound literals that get reloaded; SBCL's eql redefinition check signals DEFCONSTANT-UNEQL on the fresh object even with identical contents.
Use defparameter/defvar, or alexandria:define-constant with :test #'equal when a real constant is needed.
Resolving project fixtures/data via merge-pathnames against *load-truename*, which points into the FASL cache (or is unbound) when ASDF loads a compiled file.
Resolve through asdf:system-relative-pathname / asdf:system-source-directory.
Guarding a test-system definition with (unless (asdf:find-system ...)) so test-system recurses into the same ASD load and can surface as a circular dependency.
Define the test system unconditionally and let ASDF manage repeated ASD loads.
Use ASDF for all system definitions; never load files directly
Provide restarts for recoverable error conditions
Document all exported symbols with docstrings
Target SBCL 2.5+ features; use modern ASDF 3.3+ defsystem patterns; never use legacy DEFINE-SYSTEM forms
Use *earmuffs* for special variables, +plus-signs+ for constants
Prefer :import-from over bare :use for clear dependencies
Use check-type for argument validation at function boundaries
Consider package-inferred-system for new projects
Understand Lisp code requirements
1. Check ASDF system definition
Workflow guidance
Step completed
2. Review existing macros and patterns
Workflow guidance
Step completed
3. Identify CLOS class hierarchies
Workflow guidance
Step completed
Write idiomatic Common Lisp code
1. Use appropriate abstraction level
Workflow guidance
Step completed
2. Follow condition system for errors
Workflow guidance
Step completed
3. Design reusable macros carefully
Workflow guidance
Step completed
Verify Lisp code correctness
1. Load system with ASDF
Workflow guidance
Step completed
2. Run tests with appropriate framework
Workflow guidance
Step completed
3. Check for compilation warnings
Workflow guidance
Step completed
<error_escalation inherits="core-patterns#error_escalation">
Style inconsistency
Compilation warning or type error
Macro expansion error
Reader macro conflict
</error_escalation>
Apply this skill when task keywords and domain match
Use the canonical workflow and verify with project conventions
<decision_tree name="skill_activation">
Does the task clearly match this skill domain?
Use this skill workflow and constraints
Use a more appropriate domain skill
</decision_tree>
<related_agents>
Locate code patterns and references in this skill domain
Review implementation quality against this skill guidance
Analyze code complexity and suggest refactoring improvements
</related_agents>
Use ASDF for system definition
Follow condition system for error handling
Document macros with clear examples
Overly complex macros without documentation
Global state without clear lifecycle
Reader macros without namespace isolation
<related_skills>
Navigate CLOS hierarchies, generic functions, and symbol definitions
Access ASDF, SBCL, and Common Lisp library documentation
Debug condition handling, macro expansion, and SBCL-specific issues
Operational SBCL execution, debugger, profiling, and executable build workflows
</related_skills>