Lisp Interview Questions with Answers
Most Asked Lisp Interview Questions for Software Engineer Roles
Introduction
This page provides a complete collection of Lisp Interview Questions and Answers designed for software developers, programmers, AI engineers, functional programming enthusiasts, and candidates preparing for technical interviews. Lisp is one of the oldest and most influential programming languages. It introduced concepts such as symbolic computation, functional programming, recursion, garbage collection, and dynamic typing. Lisp is widely known for its importance in artificial intelligence, language research, and advanced software development. This interview guide covers beginner, intermediate, and advanced Lisp concepts including Lisp syntax, lists, atoms, S-expressions, recursion, functional programming, macros, lambda expressions, evaluation models, Common Lisp, Scheme, and real-world programming scenarios.
Why Lisp?
- Pioneered functional programming and recursion – a foundational language in computer science
- Powerful macro system – allows metaprogramming and code-as-data (homoiconicity)
- Symbolic computation – ideal for AI, natural language processing, and research
- Dynamic and interactive development – REPL-driven programming for rapid prototyping
- Multiple dialects – Common Lisp, Scheme, Clojure provide flexibility for different domains
- Influenced many modern languages – concepts adopted in Python, Ruby, JavaScript, and more
- Strong community and rich ecosystem for academic and industry applications
Most Asked Lisp Interview Questions
Lisp is a family of programming languages with a long history, designed for symbolic computation and artificial intelligence. It is known for its unique syntax, powerful macro system, and support for functional programming.
- Symbolic computation: Designed for manipulating symbols
- Functional programming: First-class functions
- Macros: Powerful meta-programming capabilities
- Garbage collection: Automatic memory management
- REPL: Interactive development environment
;; Hello World in Lisp
(format t "Hello, World!")Variables in Lisp are declared using defvar (global), defparameter (global mutable), and let (local).
- defvar: Global variable (mutable)
- defparameter: Global variable (mutable)
- let: Local variable
- Dynamic typing: Variables can hold any type
- setf: Used to assign values
;; Variables in Lisp
(defparameter *mutable-var* "Hello") ; Mutable variable (global)
(defvar *immutable-var* "World") ; Immutable variable (global)
(let ((local-var "Local")) ; Local variable
(format t local-var))
;; Type inference and dynamic typing
(setf *mutable-var* "Updated")
(format t *mutable-var*)Lisp has a rich set of data types including numbers, characters, strings, symbols, lists, arrays, and hash tables.
- Numbers: Integers, floats, ratios
- Booleans: t and nil
- Characters: #\A
- Strings: "Hello Lisp"
- Symbols: hello-symbol
- Lists: (1 2 3 4 5)
- Arrays: make-array
;; Data Types in Lisp
;; Numbers
(defparameter *int-num* 10)
(defparameter *float-num* 3.14)
(defparameter *ratio-num* 1/2)
;; Booleans
(defparameter *is-active* t) ; t for true
(defparameter *is-inactive* nil) ; nil for false
;; Characters
(defparameter *char* #\A)
;; Strings
(defparameter *str* "Hello Lisp")
;; Symbols
(defparameter *sym* 'hello-symbol)
;; Lists
(defparameter *list* '(1 2 3 4 5))
;; Arrays
(defparameter *array* (make-array 5 :initial-contents '(1 2 3 4 5)))
;; Type checking
(typep 10 'integer) ; tFunctions in Lisp are defined using the defun macro. They support optional parameters, multiple return values, and higher-order functions.
- Basic:
(defun name (params) body) - Documentation: Strings after params
- Optional params:
&optional - Multiple return values:
(values) - Higher-order: Functions as arguments
;; Functions in Lisp
;; Basic function
(defun add (a b)
(+ a b))
;; Function with documentation
(defun subtract (a b)
"Subtract b from a"
(- a b))
;; Default parameters not directly supported in Common Lisp
;; But can be simulated
(defun greet (&optional (name "Guest"))
(format nil "Hello, ~a!" name))
;; Function with multiple return values
(defun divide (a b)
(values (/ a b) (mod a b)))
;; Higher-order function
(defun operate (a b operation)
(funcall operation a b))
;; Lambda expression
(defparameter *multiply* (lambda (a b) (* a b)))
;; Usage
(format t "~a" (add 5 3))
(format t "~a" (subtract 10 4))
(format t "~a" (greet "Alice"))
(format t "~a" (operate 6 7 *multiply*))Arrays in Lisp are created with make-array and accessed with aref. Vectors are one-dimensional arrays.
- Creation:
(make-array size) - Access:
(aref arr index) - Modify:
(setf (aref arr index) value) - Vectors:
#(1 2 3) - Operations:
array-dimension
;; Arrays in Lisp
;; Array creation
(defparameter *numbers* (make-array 5 :initial-contents '(1 2 3 4 5)))
(defparameter *strings* (make-array 3 :initial-contents '("Apple" "Banana" "Orange")))
;; Access and modify
(aref *numbers* 2) ; Access element (0-indexed)
(setf (aref *numbers* 2) 10) ; Modify element
;; Array operations
(array-dimension *numbers* 0) ; Size
(array-dimensions *numbers*) ; Dimensions
;; Iteration
(dotimes (i (length *numbers*))
(format t "~a " (aref *numbers* i)))
;; Vector (1D array)
(defparameter *vector* #(1 2 3 4 5))
;; Map over array
(map 'vector (lambda (x) (* x 2)) *numbers*)
;; Lists vs Arrays
(defparameter *list* '(1 2 3)) ; List
(defparameter *array-2* (make-array 3 :initial-contents '(1 2 3))) ; ArrayLisp collections include lists, sets, and hash tables. Lists are the most common and versatile collection type.
- Lists:
(list 1 2 3) - Sets:
(remove-duplicates) - Maps:
make-hash-table - Operations:
filter,map,reduce - Modification:
push,pop
;; Collections in Lisp
;; Lists
(defparameter *immutable-list* '(1 2 3 4 5))
(defparameter *mutable-list* (list 1 2 3))
(setf (cdr (cdr *mutable-list*)) (list 4)) ; Modify list
;; Sets (using lists)
(defparameter *set* (remove-duplicates '(1 2 3 3))) ; (1 2 3)
;; Hash tables (maps)
(defparameter *immutable-map* '((key1 . value1) (key2 . value2)))
(defparameter *mutable-map* (make-hash-table))
(setf (gethash 'key1 *mutable-map*) "value1")
(setf (gethash 'key2 *mutable-map*) "value2")
;; Collection operations
(defparameter *numbers* '(1 2 3 4 5 6))
(defparameter *evens* (remove-if-not #'evenp *numbers*))
(defparameter *doubled* (mapcar (lambda (x) (* x 2)) *numbers*))
(defparameter *sum* (reduce #'+ *numbers*))
(defparameter *exists* (some (lambda (x) (> x 10)) *numbers*))
(defparameter *all-even* (every #'evenp *numbers*))
(format t "~a" *evens*)
(format t "~a" *doubled*)
(format t "~a" *sum*)Structs in Lisp are defined with defstruct and provide accessor functions, constructors, and copying.
- Definition:
(defstruct person name age city) - Constructor:
(make-person :name "Alice") - Accessors:
(person-name obj) - Copy:
(copy-person obj) - Default values:
(city "Unknown")
;; Structs (Data Classes) in Lisp
;; Define structure similar to data class
(defstruct person
name
age
(city "Unknown"))
;; Usage
(defparameter *person1* (make-person :name "Alice" :age 25 :city "NYC"))
(defparameter *person2* (copy-person *person1*)) ; Copy
(setf (person-age *person2*) 26) ; Modify copy
;; Accessors
(person-name *person1*)
(person-age *person1*)
(person-city *person1*)
(format t "~a" *person1*)Lisp uses CLOS (Common Lisp Object System) for object-oriented programming. Sealed classes can be simulated using generic functions and classes.
- Generic functions:
defgeneric - Classes:
defclass - Methods:
defmethod - Polymorphism: Based on class types
- Multi-methods: Multiple dispatch
;; Sealed Classes in Lisp (using CLOS)
;; Define generic function
(defgeneric handle-result (result))
;; Define classes
(defclass success ()
((data :initarg :data :accessor data)))
(defclass error ()
((message :initarg :message :accessor message)))
(defclass loading ()
())
;; Implement methods
(defmethod handle-result ((result success))
(format t "Success: ~a" (data result)))
(defmethod handle-result ((result error))
(format t "Error: ~a" (message result)))
(defmethod handle-result ((result loading))
(format t "Loading..."))
;; Sealed interfaces simulation
(defgeneric area (shape))
(defclass circle ()
((radius :initarg :radius :accessor radius)))
(defclass rectangle ()
((width :initarg :width :accessor width)
(height :initarg :height :accessor height)))
(defclass point ()
())
(defmethod area ((shape circle))
(* pi (expt (radius shape) 2)))
(defmethod area ((shape rectangle))
(* (width shape) (height shape)))
(defmethod area ((shape point))
0)
;; Usage
(handle-result (make-instance 'success :data "Data loaded"))
(format t "~a" (area (make-instance 'circle :radius 5.0)))Lisp uses nil to represent null values. Null safety is achieved through conditional checks and functions that handle nil gracefully.
- nil: Represents null/false
- Conditionals:
when,if - Safe functions:
when,unless - Elvis operator: Custom implementation
- Type checking:
typep
;; Null Safety in Lisp
;; Lisp doesn't have built-in null safety like Kotlin
;; But we can implement patterns
;; Sentinel values
(defparameter *unset* :unset)
;; Safe access function
(defun safe-length (list)
(if list (length list) 0))
;; Safe call pattern
(defun safe-call (obj &optional default)
(if obj (funcall obj) default))
;; Elvis operator equivalent
(defun elvis (value default)
(if value value default))
;; Safe casting
(defun safe-type (obj type)
(if (typep obj type) obj nil))
;; Let function for null checks
(defun process-string (str)
(when str
(format t "String is: ~a" str)
(format t "Length: ~a" (length str))))
;; Usage
(process-string "Hello")
(process-string nil)
(elvis nil "default")
(safe-type 10 'integer)Lisp provides control flow through if, cond, loop, dotimes, and do.
- If-else:
(if condition then else) - Cond:
(cond ((condition) result) ...) - Loop:
(loop for i from 1 to 10) - Dotimes:
(dotimes (i 5) body) - Do:
(do ((i 0 (1+ i))) ((>= i 5)))
;; Control Flow in Lisp
;; If-else expression
(defparameter *age* 25)
(defparameter *status* (if (< *age* 18) "Minor" "Adult"))
(format t *status*)
;; Cond (switch replacement)
(defparameter *grade* 'A)
(defparameter *result* (cond
((eq *grade* 'A) "Excellent")
((eq *grade* 'B) "Good")
((eq *grade* 'C) "Fair")
(t "Needs Improvement")))
(format t *result*)
;; Cond with ranges
(defparameter *score* 85)
(defparameter *grade2* (cond
((<= 90 *score* 100) "A")
((<= 80 *score* 89) "B")
((<= 70 *score* 79) "C")
(t "F")))
;; For loop (using dotimes)
(dotimes (i 5)
(format t "~a " i))
;; For loop with step
(loop for i from 1 to 10 by 2
do (format t "~a " i))
;; For loop down to
(loop for i from 10 downto 1
do (format t "~a " i))
;; While loop
(defparameter *i* 0)
(loop while (< *i* 5)
do (format t "~a " *i*)
(incf *i*))
;; Do-while loop
(loop do (format t "~a " *i*)
(decf *i*)
while (> *i* 0))CLOS provides a powerful object system with classes, inheritance, and multiple dispatch. Classes are defined with defclass.
- Class:
(defclass person () ((name))) - Inheritance:
(defclass dog (animal) ((breed))) - Generic functions:
defgeneric - Methods:
defmethod - Interfaces: Simulated via generic functions
;; Classes and Inheritance in Lisp (CLOS)
;; Base class
(defclass animal ()
((name :initarg :name :accessor name)))
;; Generic function
(defgeneric make-sound (animal))
;; Derived class
(defclass dog (animal)
((breed :initarg :breed :accessor breed)))
(defmethod make-sound ((animal animal))
(format t "Animal sound"))
(defmethod make-sound ((dog dog))
(format t "Woof!"))
;; Abstract class simulation
(defclass vehicle ()
((started :initform nil :accessor started)))
(defgeneric start (vehicle))
(defmethod start ((vehicle vehicle))
(setf (started vehicle) t)
(format t "Started"))
;; Interface simulation (using generic functions)
(defgeneric fly (obj))
(defgeneric land (obj))
(defgeneric swim (obj))
(defclass duck ()
())
(defmethod fly ((duck duck))
(format t "Flying"))
(defmethod land ((duck duck))
(format t "Landing..."))
(defmethod swim ((duck duck))
(format t "Swimming"))
;; Usage
(defparameter *dog* (make-instance 'dog :name "Rex" :breed "German Shepherd"))
(make-sound *dog*)
(name *dog*)
(defparameter *duck* (make-instance 'duck))
(fly *duck*)
(swim *duck*)Properties in Lisp are implemented as slots in CLOS. They can have getters, setters, and validation.
- Slots:
(name :initarg :name :accessor name) - Getters/Setters:
defmethod - Validation: Custom setters
- Lazy initialization: Check and compute
- Transformation: String manipulation in setters
;; Properties in Lisp (using CLOS)
(defclass person ()
((name :initarg :name :accessor name
:initform "")
(age :initarg :age :accessor age
:initform 0)
(email :initarg :email :accessor email
:initform "")
(address :initarg :address :accessor address
:initform nil)
(expensive-data :initform nil)))
;; Lazy initialization
(defun get-expensive-data (obj)
(unless (slot-value obj 'expensive-data)
(format t "Computing expensive data...")
(setf (slot-value obj 'expensive-data) "Expensive Result"))
(slot-value obj 'expensive-data))
;; Property with validation
(defmethod (setf age) (new-value (obj person))
(when (>= new-value 0)
(setf (slot-value obj 'age) new-value)))
;; Property with transformation
(defmethod (setf name) (new-value (obj person))
(setf (slot-value obj 'name) (string-trim " " new-value)))
(defmethod name ((obj person))
(string-upcase (slot-value obj 'name)))
;; Usage
(defparameter *person* (make-instance 'person))
(setf (name *person*) " Alice ")
(format t "~a" (name *person*)) ; ALICE
(setf (age *person*) 25)
(format t "~a" (get-expensive-data *person*))Lisp uses packages to organize code. Packages can serve as companion objects with shared constants and functions.
- Package:
defpackage - Constants:
defparameter - Factory functions:
defun create - Exports:
:export - Usage: Package-qualified symbols
;; Companion Objects in Lisp (using packages)
;; Define package as companion
(defpackage :my-class
(:use :cl)
(:export :tag :counter :create))
(in-package :my-class)
(defparameter *tag* "MyClass")
(defparameter *counter* 0)
(defun create ()
(incf *counter*)
(make-instance 'my-class))
;; Define class
(defclass my-class ()
())
;; Usage
(in-package :cl)
(print my-class::*tag*)
(my-class::create)
(my-class::create)Lisp uses handler-case and handler-bind for exception handling. Custom exceptions are defined with define-condition.
- Handler-case:
(handler-case body (error (e) ...)) - Custom exceptions:
define-condition - Unwind-protect:
unwind-protectfor finally - Signaling:
error - Restarts:
restart-case
;; Exception Handling in Lisp
;; Handler-case (try-catch)
(defun divide (a b)
(handler-case
(/ a b)
(division-by-zero ()
(format t "Division by zero!")
0)))
;; Handler-bind for more control
(defun divide-safe (a b)
(handler-case
(/ a b)
(error (e)
(format t "Error: ~a" e)
0)))
;; Custom exception
(define-condition invalid-age-exception (error)
((age :initarg :age :accessor age)
(message :initarg :message :accessor message
:initform "Invalid age"))
(:report (lambda (c stream)
(format stream "~a: ~a"
(message c) (age c)))))
(defun validate-age (age)
(if (or (< age 0) (> age 150))
(error 'invalid-age-exception :age age)
age))
;; Unwind-protect (finally)
(defun read-file ()
(let ((file (open "test.txt" :if-does-not-exist nil)))
(unwind-protect
(if file
(read-line file)
(format t "File not found"))
(when file
(close file)
(format t "Closing resources...")))))
;; Usage
(format t "~a" (divide 10 2))
(format t "~a" (divide 10 0))
(handler-case
(validate-age 200)
(invalid-age-exception (e)
(format t "~a" e)))Lambdas are anonymous functions created with lambda. They are first-class and can be passed as arguments.
- Syntax:
(lambda (x) (* x x)) - Calling:
(funcall lambda arg) - Higher-order: Functions accepting lambdas
- Function reference:
#'function-name - Closures: Captures lexical scope
;; Lambda Expressions in Lisp
;; Basic lambda
(defparameter *square* (lambda (x) (* x x)))
;; Lambda with multiple parameters
(defparameter *doubled* (lambda (x) (* x 2)))
;; Higher-order functions
(defun perform-operation (x y operation)
(funcall operation x y))
;; Lambda with multiple lines
(defparameter *complex-operation*
(lambda (x)
(let ((y (* x 2)))
(+ y 10))))
;; Function reference
(defun multiply (x y)
(* x y))
;; Returning lambda from function
(defun get-operation (type)
(cond
((eq type 'add) (lambda (a b) (+ a b)))
((eq type 'subtract) (lambda (a b) (- a b)))
(t (lambda (a b) 0))))
;; Usage
(format t "~a" (funcall *square* 5))
(format t "~a" (perform-operation 10 20 (lambda (x y) (* x y))))
(defparameter *add* (get-operation 'add))
(format t "~a" (funcall *add* 5 3))Lisp uses let, progn, and with-* macros for scope management and block execution.
- let: Local variable binding
- progn: Sequence of expressions
- with-*: Resource management
- apply: Apply function to arguments
- take-if: Conditional filtering
;; Scope Functions in Lisp
;; let - execute block
(defun process-person (person)
(when person
(let ((name (getf person :name))
(age (getf person :age)))
(format t "Name: ~a" name)
(setf (getf person :age) 26)
person)))
;; let with local functions
(defun process-with-scope ()
(let ((numbers '(1 2 3 4 5))
(sum 0))
(dolist (n numbers)
(incf sum n))
sum))
;; apply - configure object
(defun update-person (person)
(apply #'make-instance 'person
:name (getf person :name)
:age (1+ (getf person :age))
:city "SF"))
;; also - perform additional operations
(defun process-list (lst)
(progn
(format t "Before: ~a" lst)
(append lst '(4))
(format t "After: ~a" lst)
lst))
;; take-if equivalent
(defun take-if (predicate value)
(if (funcall predicate value) value nil))
;; Usage
(process-person '(:name "Alice" :age 25 :city "NYC"))
(process-with-scope)
(take-if (lambda (x) (>= x 18)) 25)Lisp uses generic functions and wrapper functions to add functionality to existing types without modification.
- Generic functions:
defgeneric - Wrapper functions: Helper functions
- Type-specific:
defmethod - Extension: Adding methods to classes
- Composition: Building on existing functions
;; Extension Functions in Lisp
;; Since Lisp doesn't have extension functions directly,
;; we use generic functions or wrapper functions
;; Using wrapper functions
(defun string-is-email (str)
(and (find #@ str) (find #. str)))
(defun string-add-prefix (str prefix)
(concatenate 'string prefix str))
(defun int-is-even (n)
(evenp n))
(defun int-is-odd (n)
(oddp n))
;; List extension
(defun list-second-or-null (lst)
(if (>= (length lst) 2)
(nth 1 lst)
nil))
;; String extension
(defun string-word-count (str)
(length (split-string str " ")))
;; Generic extension using CLOS
(defgeneric is-even (obj))
(defmethod is-even ((obj integer))
(evenp obj))
;; Usage
(string-is-email "test@example.com")
(string-add-prefix "Hello" "Greeting: ")
(int-is-even 5)
(string-word-count "Hello World")
(list-second-or-null '(1 2 3))
(is-even 4)Lisp doesn't have type aliases directly, but macros and defparameter can be used to create aliases.
- defparameter: Create aliases
- Macros:
defmacrofor aliases - Function types: Store in variables
- Complex types: Use structures or hash tables
- Readability: Improve code clarity
;; Type Aliases in Lisp
;; Lisp doesn't have type aliases directly
;; Using defparameter to create aliases
;; Function type alias
(defparameter *operation* nil) ; Used as a placeholder
;; Or using macros
(defmacro defalias (alias type)
`(defparameter ,alias ,type))
;; Usage
(defparameter *add* (lambda (a b) (+ a b)))
(defparameter *multiply* (lambda (a b) (* a b)))
(defun execute (op a b)
(funcall op a b))
;; For complex types
(defparameter *users* (make-hash-table))
(setf (gethash "user1" *users*) '("Alice" . 25))
(setf (gethash "user2" *users*) '("Bob" . 30))
;; Usage
(format t "~a" (execute *add* 5 3))
(format t "~a" (execute *multiply* 5 3))
(format t "~a" (car (gethash "user1" *users*)))Lisp macros serve as inline functions, expanding at compile time. Functions can also be declared inline with declaim.
- Macros:
defmacro - declaim:
(declaim (inline function-name)) - Performance: Reduced overhead
- Reified types: Type checking at compile time
- Use cases: Performance-critical code
;; Inline Functions in Lisp
;; Lisp macros serve a similar purpose to inline functions
;; They expand at compile time
;; Inline macro
(defmacro measure-time (&body body)
`(let ((start (get-universal-time)))
,@body
(format t "Time: ~a seconds" (- (get-universal-time) start))))
;; Noinline equivalent - functions are always noinline in Lisp
(defun regular-function ()
(format t "Regular function"))
;; Reified type parameter equivalent
(defmacro is-type (value type)
`(typep ,value ,type))
;; Usage
(measure-time
(sleep 1))
(format t "~a" (is-type "Hello" 'string))
(format t "~a" (is-type "Hello" 'integer))
;; Filter by type
(defun filter-by-type (lst type)
(remove-if-not (lambda (x) (typep x type)) lst))
(defparameter *mixed* (list 1 "Hello" 3.14 "World"))
(defparameter *strings* (filter-by-type *mixed* 'string))
(format t "~a" *strings*)Higher-order functions take functions as arguments or return functions. They are central to functional programming in Lisp.
- Parameter:
(defun apply-op (a b op) (funcall op a b)) - Return: Functions that return lambdas
- Composition:
composefunction - Callbacks: Used in event-driven code
- Functional programming: Core concept
;; Higher-Order Functions in Lisp
;; Function that takes a function as parameter
(defun apply-operation (a b operation)
(funcall operation a b))
;; Function that returns a function
(defun get-multiplier (factor)
(lambda (x) (* x factor)))
;; Function composition
(defun compose (f g)
(lambda (x) (funcall f (funcall g x))))
;; Higher-order function with multiple lambdas
(defun process (value transform filter)
(if (funcall filter value)
(funcall transform value)
nil))
;; Usage with lambda
(defparameter *result* (apply-operation 10 20 (lambda (a b) (+ a b))))
(format t "~a" *result*)
(defparameter *double* (get-multiplier 2))
(format t "~a" (funcall *double* 5))
(defparameter *square* (lambda (x) (* x x)))
(defparameter *add-ten* (lambda (x) (+ x 10)))
(defparameter *square-then-add-ten* (compose *add-ten* *square*))
(format t "~a" (funcall *square-then-add-ten* 5))
;; Using with named function
(defun add (a b) (+ a b))
(format t "~a" (apply-operation 10 20 #'add))Lisp doesn't have built-in coroutines, but they can be simulated using threads, closures, or custom implementations.
- Threads:
sb-thread:make-thread - Closures: Stateful functions
- Suspending: Custom continuation passing
- Structured concurrency: Thread management
- Dispatchers: Thread pools
;; Coroutines in Lisp (simulated with threads)
;; Lisp doesn't have built-in coroutines like Kotlin
;; Using threads as an alternative
;; Basic thread
(defun fetch-data ()
(sleep 1)
"Data loaded")
;; Launch thread
(defun main-launch ()
(let ((thread (sb-thread:make-thread
(lambda ()
(sleep 2)
(format t "Thread completed")))))
(sb-thread:join-thread thread)))
;; Async/await simulation
(defun async-fetch ()
(sb-thread:make-thread #'fetch-data))
(defun await-result (thread)
(sb-thread:join-thread thread))
;; Parallel tasks
(defun parallel-tasks ()
(let* ((task1 (async-fetch))
(task2 (async-fetch))
(results (list (await-result task1) (await-result task2))))
(format t "Results: ~a" results)))
;; Timeout
(defun with-timeout (seconds fn)
(let ((thread (sb-thread:make-thread fn)))
(sleep seconds)
(if (sb-thread:thread-alive-p thread)
(progn
(sb-thread:terminate-thread thread)
(format t "Timed out!"))
(sb-thread:join-thread thread))))
;; Usage
(main-launch)
(async-fetch)
(parallel-tasks)Lisp uses streams and custom functions to simulate flows. Streams can be filtered, mapped, and collected.
- Streams: Stateful functions
- Operators:
map,filter - StateFlow: State management
- SharedFlow: Shared streams
- Collect: Collection of stream values
;; Flows in Lisp (simulated with streams)
;; Lisp doesn't have built-in flows like Kotlin
;; Using streams and custom functions
;; Simple stream
(defun make-number-stream (n)
(let ((i 1))
(lambda ()
(if (<= i n)
(prog1 i (incf i))
nil))))
;; Stream operators
(defun stream-filter (stream pred)
(lambda ()
(loop
(let ((val (funcall stream)))
(if (null val)
(return nil)
(if (funcall pred val)
(return val)))))))
(defun stream-map (stream fn)
(lambda ()
(let ((val (funcall stream)))
(if (null val)
nil
(funcall fn val)))))
(defun stream-collect (stream)
(loop for val = (funcall stream)
while val
collect val))
;; Usage
(defparameter *numbers* (make-number-stream 5))
(defparameter *filtered* (stream-filter *numbers* (lambda (x) (evenp x))))
(defparameter *mapped* (stream-map *filtered* (lambda (x) (format nil "Number: ~a" x))))
(format t "~a" (stream-collect *mapped*))
;; State simulation
(defparameter *state* 0)
(defun get-state () *state*)
(defun set-state (val) (setf *state* val))
(defun increment () (incf *state*))Channels can be implemented using queues and threads for communication between different threads.
- Queue:
defclass queue - Send/Receive:
queue-enqueue,queue-dequeue - Buffered: Queue capacity
- Producer/Consumer: Patterns for communication
- Close: Notify when done
;; Channels in Lisp (using queues)
;; Lisp doesn't have built-in channels like Kotlin
;; Using queues and threads
;; Simple queue implementation
(defclass queue ()
((items :initform '() :accessor items)
(lock :initform (sb-thread:make-mutex) :accessor lock)))
(defun queue-enqueue (queue item)
(sb-thread:with-mutex ((lock queue))
(setf (items queue) (append (items queue) (list item)))))
(defun queue-dequeue (queue)
(sb-thread:with-mutex ((lock queue))
(let ((item (car (items queue))))
(setf (items queue) (cdr (items queue)))
item)))
(defun queue-size (queue)
(length (items queue)))
;; Basic channel
(defun basic-channel ()
(let ((queue (make-instance 'queue)))
(sb-thread:make-thread
(lambda ()
(queue-enqueue queue "Hello")
(queue-enqueue queue "World")))
(loop for i from 1 to 2
do (format t "~a" (queue-dequeue queue)))))
;; Buffered channel
(defun buffered-channel ()
(let ((queue (make-instance 'queue)))
(sb-thread:make-thread
(lambda ()
(dotimes (i 5)
(queue-enqueue queue i)
(format t "Sent: ~a" i))))
(dotimes (i 5)
(sleep 0.1)
(format t "Received: ~a" (queue-dequeue queue)))))Lisp uses CLOS classes and generic functions to implement similar patterns. Enums are typically represented as symbols or keyword arguments.
- Enum:
defparameter *colors* '(:red :green :blue) - Sealed: Classes with specific methods
- Properties: Slots with values
- Subclasses: Inheritance hierarchy
- Polymorphism: Generic functions
;; Sealed Classes and Enum Classes in Lisp
;; Enum simulation
(defparameter *colors* '(:red :green :blue))
(defparameter *status-codes* '((:success . 200)
(:error . 500)
(:loading . 100)))
;; Sealed class simulation using generic functions
(defclass ui-state ()
())
(defclass success (ui-state)
((data :initarg :data :accessor data)))
(defclass error (ui-state)
((message :initarg :message :accessor message)))
(defclass loading (ui-state)
())
(defclass idle (ui-state)
())
(defgeneric handle-state (state))
(defmethod handle-state ((state success))
(format t "Data: ~a" (data state)))
(defmethod handle-state ((state error))
(format t "Error: ~a" (message state)))
(defmethod handle-state ((state loading))
(format t "Loading..."))
(defmethod handle-state ((state idle))
(format t "Idle"))
;; Payment classes
(defclass payment () ())
(defclass cash (payment)
((amount :initarg :amount :accessor amount)))
(defclass credit-card (payment)
((number :initarg :number :accessor number)
(expiry :initarg :expiry :accessor expiry)))
(defclass paypal (payment)
((email :initarg :email :accessor email)))
(defgeneric handle-payment (payment))
(defmethod handle-payment ((payment cash))
(format t "Cash amount: ~a" (amount payment)))
(defmethod handle-payment ((payment credit-card))
(format t "Card: ~a" (number payment)))
(defmethod handle-payment ((payment paypal))
(format t "PayPal: ~a" (email payment)))Lisp is dynamically typed, so generics aren't needed. However, type checking and constraints can be implemented with typep.
- Dynamic typing: Values can be any type
- Type checking:
typep - Constraints: Validation in functions
- Variance: Not applicable
- Polymorphism: Generic functions
;; Generics in Lisp
;; Lisp is dynamically typed, so generics are less needed
;; But we can implement some patterns
;; Generic class using CLOS
(defclass box ()
((value :initarg :value :accessor value)))
;; Generic function
(defun swap (first second)
(values second first))
;; Generic with constraints
(defun sum-numbers (items)
(apply #'+ items))
;; Variance - using classes
(defclass producer ()
((produce :initarg :produce :accessor produce)))
(defclass consumer ()
((consume :initarg :consume :accessor consume)))
;; Invariant
(defclass transformer ()
((transform :initarg :transform :accessor transform)))
;; Generic function with type checking
(defun process-sequence (seq)
(mapcar (lambda (x) (format nil "~a" x)) seq))
;; Usage
(defparameter *box* (make-instance 'box :value "Hello"))
(value *box*)
(multiple-value-bind (a b) (swap 1 2)
(format t "~a, ~a" a b))
(sum-numbers '(1 2 3 4 5))Delegation in Lisp is implemented using composition and forwarding methods. Classes can delegate to other objects.
- Composition: Using instance variables
- Forwarding: Methods that call delegates
- Lazy initialization: Compute on demand
- Observable: Custom delegation patterns
- Vetoable: Validation before delegation
;; Delegation in Lisp
;; Class delegation using composition
(defclass repository ()
())
(defgeneric get-data (repo))
(defgeneric save-data (repo data))
(defclass database-repository (repository)
())
(defmethod get-data ((repo database-repository))
"Data from database")
(defmethod save-data ((repo database-repository) data)
(format t "Saving to database: ~a" data))
;; Delegation
(defclass cached-repository (repository)
((delegate :initarg :delegate :accessor delegate)
(cache :initform nil :accessor cache)))
(defmethod get-data ((repo cached-repository))
(or (cache repo)
(let ((data (get-data (delegate repo))))
(setf (cache repo) data)
data)))
;; Property delegation (lazy)
(defclass lazy-property ()
((expensive-value :initform nil :accessor expensive-value)))
(defun get-expensive-value (obj)
(unless (slot-value obj 'expensive-value)
(format t "Computing...")
(setf (slot-value obj 'expensive-value) "Result"))
(slot-value obj 'expensive-value))
;; Observable property
(defclass observable-property ()
((name :initform "Initial" :accessor name)))
(defgeneric set-name (obj value))
(defmethod set-name ((obj observable-property) value)
(let ((old (name obj)))
(setf (slot-value obj 'name) value)
(format t "~a -> ~a" old value)))
;; Usage
(defparameter *db* (make-instance 'database-repository))
(defparameter *cached* (make-instance 'cached-repository :delegate *db*))
(get-data *cached*)
(get-data *cached*)
(defparameter *lazy* (make-instance 'lazy-property))
(get-expensive-value *lazy*)
(get-expensive-value *lazy*)Lisp implements singletons using closures or packages. A closure can encapsulate private state and provide controlled access.
- Closure: Function with private state
- Package: Namespace with global state
- Thread-safe: Locking mechanisms
- Global access: Through exported symbols
- Initialization: Automatic on load
;; Object Declarations and Singletons in Lisp
;; Singleton using closure
(defun make-app-config ()
(let ((api-url "https://api.example.com")
(timeout 5000))
(lambda (command &optional value)
(cond
((eq command 'get-api-url) api-url)
((eq command 'get-timeout) timeout)
((eq command 'print-config)
(format t "API URL: ~a~%Timeout: ~a" api-url timeout))
(t nil)))))
(defparameter *app-config* (make-app-config))
;; Singleton using package
(defpackage :app-config
(:use :cl)
(:export :api-url :timeout :print-config))
(in-package :app-config)
(defparameter *api-url* "https://api.example.com")
(defparameter *timeout* 5000)
(defun print-config ()
(format t "API URL: ~a~%Timeout: ~a" *api-url* *timeout*))
(in-package :cl)
;; Usage
(funcall *app-config* 'get-api-url)
(funcall *app-config* 'print-config)
app-config::*api-url*Lisp macros are perfect for creating DSLs. They allow custom syntax and compile-time code generation.
- Macros:
defmacro - Builder pattern: Configure with macros
- Lambdas: For custom syntax
- Scope functions:
with-*macros - Type-safe builders: HTML, XML, etc.
;; DSL (Domain Specific Language) in Lisp
;; HTML DSL
(defmacro html (&body body)
`(with-output-to-string (*standard-output*)
,@body))
(defmacro body (&body body)
`(progn
(format t "<body>")
,@body
(format t "</body>")))
(defmacro h1 (text)
`(format t "<h1>~a</h1>" ,text))
(defmacro p (text)
`(format t "<p>~a</p>" ,text))
;; Builder pattern
(defclass user-builder ()
((name :initform "" :accessor name)
(age :initform 0 :accessor age)
(email :initform "" :accessor email)))
(defun build-user (builder)
(list :name (name builder)
:age (age builder)
:email (email builder)))
;; User creation function
(defun user (&rest args)
(let ((builder (make-instance 'user-builder)))
(dolist (arg args)
(cond
((eq (car arg) :name) (setf (name builder) (cdr arg)))
((eq (car arg) :age) (setf (age builder) (cdr arg)))
((eq (car arg) :email) (setf (email builder) (cdr arg)))))
(build-user builder)))
;; Usage
(html
(body
(h1 "Welcome to Lisp DSL")
(p "This is a paragraph")
(p "Another paragraph")))
(user :name "Alice" :age 25 :email "alice@example.com")Lisp uses metadata and macros for annotations. Functions can have metadata attached using get and setf.
- Metadata:
(setf (get symbol 'annotations) value) - Macros:
defmacrofor custom annotations - Reflection: Inspecting metadata
- Repeatable: Multiple annotations
- Usage:
getto retrieve
;; Annotations in Lisp (using meta-programming)
;; Lisp doesn't have built-in annotations like Kotlin
;; Using macros and metadata
;; Custom annotation macro
(defmacro defannotated (name &rest body)
`(progn
(defparameter ,name
(list :annotations ',(car body) :value ,(cadr body)))))
;; Using metadata
(defun annotate (obj &rest annotations)
(setf (get obj 'annotations) annotations))
(defun get-annotations (obj)
(get obj 'annotations))
;; Example usage
(defun annotated-method ()
(format t "Annotated method"))
(annotate 'annotated-method :value "test")
;; Reflection simulation
(defun read-annotations (symbol)
(get symbol 'annotations))
;; Repeatable annotations
(defun add-annotation (symbol annotation)
(push annotation (get symbol 'annotations)))
(add-annotation 'annotated-method :permission "read")
(add-annotation 'annotated-method :permission "write")
;; Usage
(annotated-method)
(read-annotations 'annotated-method)Lisp provides reflection through CLOS and the Meta-Object Protocol (MOP). Classes, slots, and methods can be inspected at runtime.
- Class:
class-name,class-of - Slots:
class-slots - Methods:
generic-function-methods - Call:
funcallfor dynamic calls - Constructors:
make-instance
;; Reflection in Lisp
;; Reflection capabilities using CLOS and meta-object protocol
;; Class for reflection examples
(defclass person ()
((name :initarg :name :accessor name)
(age :initarg :age :accessor age)
(city :initarg :city :accessor city
:initform "Unknown")))
(defmethod greet ((obj person))
(format nil "Hello, my name is ~a" (name obj)))
(defmethod update-age ((obj person) new-age)
(setf (age obj) new-age))
;; Basic reflection
(defun basic-reflection ()
(let ((person (make-instance 'person :name "Alice" :age 25)))
(format t "Class: ~a" (class-name (class-of person)))
(format t "Slots: ~a" (closer-mop:class-slots (class-of person)))))
;; Accessing slots
(defun access-slots ()
(let ((person (make-instance 'person :name "Alice" :age 25)))
(dolist (slot (closer-mop:class-slots (class-of person)))
(let ((name (closer-mop:slot-definition-name slot)))
(format t "~a = ~a" name (slot-value person name))))))
;; Calling functions
(defun call-functions ()
(let ((person (make-instance 'person :name "Alice" :age 25)))
(format t "~a" (greet person))
(update-age person 30)
(format t "Updated age: ~a" (age person))))
;; Create instance
(defun create-instance ()
(make-instance 'person :name "Bob" :age 30 :city "NYC"))Threads in Lisp can be used with different priorities and thread-local storage for context management.
- Threads:
sb-thread:make-thread - Context:
*thread-local* - Dispatcher: Thread pools
- Custom context: Named threads
- Thread-local:
make-hash-table
;; Coroutine Context and Dispatchers in Lisp
;; Using threads with different priorities
;; Different dispatchers simulation
(defun dispatcher-example ()
(let ((threads '()))
;; Default
(push (sb-thread:make-thread
(lambda () (format t "Default: ~a" (sb-thread:thread-name sb-thread:*current-thread*))))
threads)
;; IO
(push (sb-thread:make-thread
(lambda () (format t "IO: ~a" (sb-thread:thread-name sb-thread:*current-thread*))))
threads)
;; Main (current thread)
(format t "Main: ~a" (sb-thread:thread-name sb-thread:*current-thread*))
(dolist (thread threads)
(sb-thread:join-thread thread))))
;; Custom context
(defun custom-context ()
(sb-thread:make-thread
(lambda ()
(format t "Context: Custom"))))
;; ThreadLocal simulation
(defparameter *thread-local* (make-hash-table))
(defun get-thread-local ()
(gethash (sb-thread:thread-name sb-thread:*current-thread*) *thread-local*))
(defun set-thread-local (value)
(setf (gethash (sb-thread:thread-name sb-thread:*current-thread*) *thread-local*) value))
(defun thread-local-example ()
(set-thread-local "Main")
(let ((thread (sb-thread:make-thread
(lambda ()
(set-thread-local "IO")
(format t "ThreadLocal: ~a" (get-thread-local))))))
(sb-thread:join-thread thread))
(format t "ThreadLocal restored: ~a" (get-thread-local)))Lisp streams can be transformed using functions like mapcar, remove-if-not, and custom stream operators.
- filter:
remove-if-not - map:
mapcar - buffer: Custom buffering
- conflate: Drop intermediate values
- collect latest: Latest value processing
;; Flow Operators and Transformations in Lisp
;; Stream transformations using functions
;; Simple stream
(defun make-number-stream (n)
(let ((i 0))
(lambda ()
(if (< i n)
(prog1 i (incf i))
nil))))
;; Basic flow transformation
(defun flow-transform-example ()
(let ((stream (make-number-stream 10)))
(loop for val = (funcall stream)
while val
when (evenp val)
do (format t "Number ~a" val))))
;; Flow with buffer
(defun flow-buffer-example ()
(let ((stream (make-number-stream 5)))
(loop for val = (funcall stream)
while val
do (format t "~a" val))))
;; Flow with conflate (drop intermediate values)
(defun flow-conflate-example ()
(let ((stream (make-number-stream 10)))
(loop for val = (funcall stream)
while val
do (progn
(sleep 0.1)
(format t "~a" val)))))
;; Flow with collect latest
(defun flow-collect-latest-example ()
(let ((stream (make-number-stream 10)))
(loop for val = (funcall stream)
while val
do (progn
(format t "Processing ~a" val)
(sleep 0.1)
(format t "Done ~a" val)))))
;; FlatMap (flattening streams)
(defun flow-flatmap-example ()
(let ((stream (make-number-stream 3)))
(loop for val = (funcall stream)
while val
do (loop for letter in '(a b)
do (format t "~a-~a" val letter)))))Thread management in Lisp provides lifecycle control. Threads can be created, joined, and terminated.
- Scope: Custom thread managers
- Lifecycle:
sb-thread:make-thread - GlobalScope: Application-wide threads
- SupervisorJob: Independent job hierarchy
- Cancellation: Thread termination
;; Coroutine Scopes and Lifecycle in Lisp
;; Using thread pools and managers
;; Custom scope with job
(defclass my-scope ()
((threads :initform '() :accessor threads)
(cancelled :initform nil :accessor cancelled)))
(defun scope-launch (scope fn)
(push (sb-thread:make-thread fn) (threads scope)))
(defun scope-cancel (scope)
(setf (cancelled scope) t)
(dolist (thread (threads scope))
(sb-thread:terminate-thread thread)))
;; Lifecycle-aware scope
(defclass lifecycle-scope (my-scope)
())
(defun launch-when-created (scope fn)
(scope-launch scope fn))
(defun on-destroy (scope)
(scope-cancel scope))
;; GlobalScope vs CoroutineScope
(defun scope-comparison ()
;; GlobalScope - runs until complete
(sb-thread:make-thread
(lambda ()
(sleep 1)
(format t "GlobalScope")))
;; CoroutineScope - tied to parent
(let ((scope (make-instance 'my-scope)))
(scope-launch scope
(lambda ()
(sleep 1)
(format t "CoroutineScope")))
(sleep 2)
(scope-cancel scope)))
;; Scope with timeout
(defun timeout-scope (seconds fn)
(let ((thread (sb-thread:make-thread fn)))
(sleep seconds)
(if (sb-thread:thread-alive-p thread)
(progn
(sb-thread:terminate-thread thread)
(format t "Timed out")
nil)
(sb-thread:join-thread thread))))Lisp uses handler-case and handler-bind for exception handling in threads.
- Try-catch:
handler-case - ExceptionHandler: Custom handlers
- SupervisorJob: Isolate failures
- Flow catch:
handler-case - SupervisorScope:
handler-case
;; Coroutine Exception Handling in Lisp
;; Using handler-case and handler-bind
;; Try-catch in thread
(defun try-catch-example ()
(handler-case
(sb-thread:make-thread
(lambda ()
(error "Error")))
(error (e)
(format t "Caught: ~a" e))))
;; Exception handler
(defun exception-handler ()
(handler-case
(sb-thread:make-thread
(lambda ()
(error "Test")))
(error (e)
(format t "Handler caught: ~a" e))))
;; Supervisor job for child isolation
(defun supervisor-example ()
(let ((threads '()))
;; Child 1
(push (sb-thread:make-thread
(lambda ()
(handler-case
(progn
(sleep 0.1)
(error "Child 1 error"))
(error (e)
(format t "Child 1 caught: ~a" e)))))
threads)
;; Child 2
(push (sb-thread:make-thread
(lambda ()
(sleep 0.2)
(format t "Child 2 still running")))
threads)
(dolist (thread threads)
(sb-thread:join-thread thread))))
;; Flow exception handling
(defun flow-exception ()
(handler-case
(progn
(format t "~a" 1)
(error "Flow error"))
(error (e)
(format t "Flow caught: ~a" e)
(format t "-1"))))
;; Supervisor scope
(defun supervisor-scope-example ()
(let ((threads '()))
(push (sb-thread:make-thread
(lambda ()
(error "Error")))
threads)
(push (sb-thread:make-thread
(lambda ()
(sleep 0.1)
(format t "Still running")))
threads)
(dolist (thread threads)
(sb-thread:join-thread thread))))Lisp can implement producer-consumer patterns using queues and threads.
- Producer-consumer: Single producer, single consumer
- Fan-out: Multiple consumers
- Fan-in: Multiple producers
- Pipelines: Chained operations
- Buffer: Queue capacity
;; Channel Producers and Consumer Patterns in Lisp
;; Using queues for producer-consumer patterns
;; Producer-Consumer pattern
(defun producer-consumer ()
(let ((queue (make-instance 'queue)))
;; Producer
(sb-thread:make-thread
(lambda ()
(dotimes (i 20)
(queue-enqueue queue i)
(format t "Produced: ~a" i)
(sleep 0.1))))
;; Consumer
(sb-thread:make-thread
(lambda ()
(dotimes (i 20)
(let ((val (queue-dequeue queue)))
(when val
(format t "Consumed: ~a" val)
(sleep 0.15))))))))
;; Fan-out pattern
(defun fan-out-example ()
(let ((queue (make-instance 'queue)))
;; Producer
(sb-thread:make-thread
(lambda ()
(dotimes (i 20)
(queue-enqueue queue i))))
;; Multiple consumers
(dotimes (id 3)
(sb-thread:make-thread
(lambda ()
(dotimes (i 20)
(let ((val (queue-dequeue queue)))
(when val
(format t "Consumer ~a: ~a" id val)
(sleep 0.1)))))))))
;; Fan-in pattern
(defun fan-in-example ()
(let ((queue (make-instance 'queue)))
;; Multiple producers
(dotimes (id 3)
(sb-thread:make-thread
(lambda ()
(dotimes (i 5)
(queue-enqueue queue (format nil "Producer ~a: ~a" id i))
(sleep 0.05)))))
;; Single consumer
(sb-thread:make-thread
(lambda ()
(dotimes (i 15)
(format t "~a" (queue-dequeue queue)))))))Lisp threads support cancellation through sb-thread:terminate-thread and cooperative cancellation using flags.
- Check flag:
when cancelled (return) - Cancel:
sb-thread:terminate-thread - Finally:
unwind-protect - NonCancellable: Custom flag
- Timeout:
sleepand termination
;; Coroutine Cancellation in Lisp
;; Thread termination and cooperative cancellation
;; Cooperative cancellation
(defun cooperative-cancellation ()
(let ((cancelled nil))
(sb-thread:make-thread
(lambda ()
(dotimes (i 100)
(when cancelled (return))
(format t "Working: ~a" i)
(sleep 0.05))))
(sleep 0.2)
(setf cancelled t)))
;; Cancellation with finally
(defun cancellation-finally ()
(let ((cancelled nil))
(sb-thread:make-thread
(lambda ()
(unwind-protect
(dotimes (i 100)
(when cancelled (return))
(format t "Processing: ~a" i)
(sleep 0.1))
(format t "Cleaning up")
(sleep 0.1)
(format t "Cleanup done"))))
(sleep 0.25)
(setf cancelled t)))
;; Cancellation with timeout
(defun cancellation-timeout ()
(let ((thread (sb-thread:make-thread
(lambda ()
(dotimes (i 10)
(sleep 0.2)
(format t "Iteration: ~a" i))))))
(sleep 1)
(sb-thread:terminate-thread thread)
(format t "Timed out")))
;; Custom cancellation check
(defun custom-cancellation ()
(let ((cancelled nil))
(sb-thread:make-thread
(lambda ()
(let ((i 0))
(loop while (and (not cancelled) (< i 1000))
do (when (= (mod i 100) 0)
(format t "Still running: ~a" i))
(incf i)
(sleep 0.001)))))
(sleep 0.1)
(setf cancelled t)))Lisp testing frameworks like fiveam or lisp-unit can test threaded code with assertions.
- runTest: Custom test runner
- advanceTimeBy:
sleepfor timing - advanceUntilIdle: Wait for threads
- TestDispatcher: Custom thread management
- Assertions:
assert
;; Testing Coroutines in Lisp
;; Using unit testing frameworks
;; Basic test function
(defun test-coroutine ()
(let ((result nil))
(sb-thread:make-thread
(lambda ()
(sleep 1)
(setf result "Success")))
(sleep 1.5)
(assert (equal result "Success") () "Test failed")))
;; Test with delay
(defun test-with-delay ()
(let ((result nil))
(sb-thread:make-thread
(lambda ()
(sleep 1)
(setf result "Done")))
;; Simulate advance time
(sleep 1)
(assert (equal result "Done") () "Test failed")))
;; Test multiple coroutines
(defun test-multiple-coroutines ()
(let ((results '()))
(sb-thread:make-thread
(lambda ()
(sleep 0.5)
(push "Task 1" results)))
(sb-thread:make-thread
(lambda ()
(sleep 0.3)
(push "Task 2" results)))
(sleep 1)
(assert (equal results '("Task 2" "Task 1")) () "Test failed")))
;; Test flow
(defun test-flow ()
(let ((values '()))
(dotimes (i 2)
(push i values))
(assert (equal values '(1 0)) () "Test failed")))
;; Time control test
(defun time-control-test ()
(let ((counter 0)
(running t))
(sb-thread:make-thread
(lambda ()
(loop while running
do (sleep 1)
(incf counter))))
(sleep 3)
(setf running nil)
(assert (<= counter 4) () "Test failed")))Lisp implementations can be ported across platforms. Feature flags (#+sbcl, #+clisp) handle platform-specific code.
- Feature flags:
#+sbcl,#+clisp - Platform-specific: Different implementations
- Serialization:
format - Cross-platform: Portable code
- Implementation: SBCL, CLISP, CCL, ABCL
;; Lisp Multiplatform (different implementations)
;; Using feature flags and different implementations
;; Platform-specific code
(defun platform-name ()
#+sbcl "SBCL"
#+clisp "CLISP"
#+ccl "CCL"
#+abcl "ABCL"
#-(or sbcl clisp ccl abcl) "Unknown")
(defun greet ()
(format nil "Hello from ~a" (platform-name)))
;; Platform-specific class simulation
(defclass platform ()
())
(defmethod get-version ((obj platform))
#+sbcl "2.2.0"
#+clisp "2.49"
#+ccl "1.12"
#+abcl "1.8.0"
#-(or sbcl clisp ccl abcl) "Unknown")
;; Platform info
(defclass platform-info ()
())
(defun get-info ()
(format nil "~a version ~a"
(greet) (get-version (make-instance 'platform))))
;; Multiplatform with serialization
(defun encode-user (user)
(format nil "~a~%~a~%~a"
(getf user :id)
(getf user :name)
(getf user :email)))
(defun decode-user (data)
(let ((lines (split-string data #Newline)))
(list :id (parse-integer (first lines))
:name (second lines)
:email (third lines))))Reverse a string using reverse or manual iteration.
- Built-in:
(reverse str) - Manual:
(with-output-to-string ...) - Complexity: O(n) time
;; Reverse a string
(defun reverse-string (str)
(reverse str))
(format t "~a" (reverse-string "hello")) ; "olleh"
;; Using loop
(defun reverse-string-loop (str)
(with-output-to-string (s)
(do ((i (1- (length str)) (1- i)))
((< i 0))
(write-char (char str i) s))))Check if a string is a palindrome using reverse or two-pointer approach.
- Method:
(string= cleaned (reverse cleaned)) - Two-pointer: Compare from both ends
- Case insensitive:
string-downcase - Ignore non-alphanumeric:
remove-if-not #'alphanumericp
;; Check palindrome
(defun is-palindrome (str)
(let ((cleaned (string-downcase
(remove-if-not #'alphanumericp str))))
(string= cleaned (reverse cleaned))))
(format t "~a" (is-palindrome "racecar")) ; t
(format t "~a" (is-palindrome "hello")) ; nil
;; Two-pointer approach
(defun is-palindrome-two-pointer (str)
(let ((cleaned (string-downcase
(remove-if-not #'alphanumericp str)))
(left 0)
(right (1- (length str))))
(loop while (< left right)
do (if (char/= (char cleaned left) (char cleaned right))
(return nil)
(progn (incf left) (decf right))))))Find maximum value using reduce #'max or manual iteration.
- Built-in:
(reduce #'max arr) - Manual: Iterate and track max
- Empty array: Handle with
error - Complexity: O(n) time
;; Find max in array
(defun find-max (arr)
(if (null arr)
(error "Empty array")
(reduce #'max arr)))
(format t "~a" (find-max '(1 5 3 9 2))) ; 9
;; Manual implementation
(defun find-max-manual (arr)
(let ((max-val (car arr)))
(dolist (num (cdr arr))
(when (> num max-val)
(setf max-val num)))
max-val))Remove duplicates using remove-duplicates or manual set implementation.
- Built-in:
(remove-duplicates arr) - Manual: Track seen elements
- Complexity: O(n²) or O(n)
;; Remove duplicates
(defun remove-duplicates-list (arr)
(remove-duplicates arr))
(format t "~a" (remove-duplicates-list '(1 2 2 3 3 4))) ; (1 2 3 4)
;; Using set
(defun remove-duplicates-set (arr)
(coerce (make-hash-table) 'list))Merge arrays using append or manual concatenation.
- Built-in:
(append arr1 arr2) - Alternative:
(concatenate 'list arr1 arr2) - Unique:
remove-duplicates
;; Merge arrays
(defun merge-arrays (arr1 arr2)
(append arr1 arr2))
(format t "~a" (merge-arrays '(1 2) '(3 4))) ; (1 2 3 4)
;; Alternative
(defun merge-arrays-plus (arr1 arr2)
(append arr1 arr2))Convert using parse-integer or safe conversion.
- toInt:
(parse-integer str) - Safe:
handler-case - Error handling: Return nil on failure
;; Convert string to number
(defun string-to-number (str)
(parse-integer str))
(format t "~a" (string-to-number "42")) ; 42
;; Safe conversion
(defun string-to-number-safe (str)
(handler-case
(parse-integer str)
(error () nil)))Iterate through alist using dolist or mapc.
- dolist:
(dolist (pair alist) ...) - mapc:
(mapc (lambda (pair) ...) alist) - Keys:
mapcar #'car - Values:
mapcar #'cdr
;; Loop through map (alist)
(defun loop-map (alist)
(dolist (pair alist)
(format t "~a => ~a" (car pair) (cdr pair))))
;; Alternative
(defun loop-map-alternate (alist)
(mapc (lambda (pair)
(format t "~a => ~a" (car pair) (cdr pair)))
alist))
(loop-map '((name . "Alice") (age . 25) (city . "NYC")))Delay using sleep or threads.
- Sleep:
(sleep (/ delay-ms 1000)) - Thread:
sb-thread:make-thread - Async:
sb-thread:make-thread
;; Delay function execution
(defun delayed-execution (delay-ms fn)
(sleep (/ delay-ms 1000))
(funcall fn))
;; Example usage
(delayed-execution 2000
(lambda ()
(format t "After 2 seconds")))
;; Using threads
(defun delayed-execution-thread (delay-ms fn)
(sb-thread:make-thread
(lambda ()
(sleep (/ delay-ms 1000))
(funcall fn))))Make HTTP GET using drakma:http-request or url-request.
- Drakma:
(drakma:http-request url) - URL-Request:
(url-request:url-request url) - Stream reading:
read-line
;; HTTP GET request
(defun fetch-data (url)
(let ((stream (drakma:http-request url)))
(when stream
(with-output-to-string (s)
(do ((line (read-line stream nil nil)
(read-line stream nil nil)))
((null line))
(write-line line s))))))
;; Alternative using URL-REQUES
(defun fetch-data-url (url)
(let ((stream (url-request:url-request url)))
(if stream
(with-output-to-string (s)
(do ((line (read-line stream nil nil)
(read-line stream nil nil)))
((null line))
(write-line line s)))
nil)))Create a Deferred using threads and state management.
- Thread:
sb-thread:make-thread - await:
sb-thread:join-thread - Error handling:
handler-case
;; Create a promise-like Deferred
(defun create-deferred (should-resolve)
(let ((thread nil)
(result nil))
(setf thread (sb-thread:make-thread
(lambda ()
(sleep 1)
(if should-resolve
(setf result "Success!")
(error "Failed!")))))
(lambda ()
(sb-thread:join-thread thread)
result)))
;; Usage
(defparameter *deferred* (create-deferred t))
(handler-case
(format t "~a" (funcall *deferred*))
(error (e)
(format t "Caught: ~a" e)))Calculate factorial using recursion or iteration.
- Recursive:
(if (<= n 1) 1 (* n (factorial (- n 1)))) - Iterative:
doloop - Edge cases: 0! = 1
;; Factorial
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
(format t "~a" (factorial 5)) ; 120
;; Iterative version
(defun factorial-iterative (n)
(do ((i 2 (1+ i))
(result 1 (* result i)))
((> i n) result)))Calculate Fibonacci using recursion, iteration, or memoization.
- Recursive:
(if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2)))) - Iterative:
doloop - Memoization:
memoize
;; Fibonacci
(defun fibonacci (n)
(if (<= n 1)
n
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
(format t "~a" (fibonacci 8)) ; 21
;; Iterative version
(defun fibonacci-iterative (n)
(do ((a 0 b)
(b 1 (+ a b))
(i 2 (1+ i)))
((> i n) b)))FizzBuzz using cond for modular arithmetic.
- When expression:
cond - Order: Check 15 first
- Range:
dotimes
;; FizzBuzz
(defun fizzbuzz (n)
(dotimes (i n)
(let ((num (1+ i)))
(cond
((zerop (mod num 15)) (format t "FizzBuzz"))
((zerop (mod num 3)) (format t "Fizz"))
((zerop (mod num 5)) (format t "Buzz"))
(t (format t "~a" num))))))
(fizzbuzz 15)Find missing number using formula or XOR.
- Formula:
(- total sum) - XOR method: XOR all numbers and indices
- Edge cases: Empty array
;; Find missing number
(defun find-missing (arr)
(let* ((n (1+ (length arr)))
(total (/ (* n (1+ n)) 2))
(sum (reduce #'+ arr)))
(- total sum)))
(format t "~a" (find-missing '(1 2 4 5 6))) ; 3Find duplicates using a set or hash table.
- Set method: Track seen elements
- Filter:
remove-if - Group by:
group-bysimulation
;; Find duplicates
(defun find-duplicates (arr)
(let ((seen '())
(duplicates '()))
(dolist (item arr)
(if (member item seen)
(push item duplicates)
(push item seen)))
duplicates))
(format t "~a" (find-duplicates '(1 2 3 2 4 3))) ; (3 2)Calculate sum using reduce or manual iteration.
- Built-in:
(reduce #'+ arr) - Manual:
dolist - Empty array: Returns 0
;; Sum of array
(defun sum-array (arr)
(reduce #'+ arr))
(format t "~a" (sum-array '(1 2 3 4 5))) ; 15
;; Manual implementation
(defun sum-array-manual (arr)
(let ((sum 0))
(dolist (num arr)
(incf sum num))
sum))Calculate average using sum divided by length.
- Method:
(/ (reduce #'+ arr) (length arr)) - Empty array: Handle with if check
- Precision: Returns rational
;; Average of array
(defun average-array (arr)
(/ (reduce #'+ arr) (length arr)))
(format t "~a" (average-array '(1 2 3 4 5))) ; 3
;; Manual implementation
(defun average-array-manual (arr)
(/ (sum-array-manual arr) (length arr)))Sort using sort with #'<.
- Non-mutating:
(sort (copy-seq arr) #'<) - Mutating:
(sort arr #'<) - Complexity: O(n log n)
;; Sort array ascending
(defun sort-ascending (arr)
(sort (copy-seq arr) #'<))
(format t "~a" (sort-ascending '(5 2 8 1 9))) ; (1 2 5 8 9)
;; In-place sorting
(defun sort-ascending-in-place (arr)
(sort arr #'<))Sort descending using sort with #'>.
- Non-mutating:
(sort (copy-seq arr) #'>) - Mutating:
(sort arr #'>) - Complexity: O(n log n)
;; Sort array descending
(defun sort-descending (arr)
(sort (copy-seq arr) #'>))
(format t "~a" (sort-descending '(5 2 8 1 9))) ; (9 8 5 2 1)
;; In-place sorting
(defun sort-descending-in-place (arr)
(sort arr #'>))Flatten using recursion or mapcan.
- Recursive: Check if listp
- mapcan:
(mapcan #'flatten-array arr) - Complexity: O(n) time
;; Flatten nested array
(defun flatten-array (arr)
(cond
((null arr) nil)
((listp (car arr))
(append (flatten-array (car arr))
(flatten-array (cdr arr))))
(t (cons (car arr) (flatten-array (cdr arr))))))
(format t "~a" (flatten-array '(1 (2 (3 4) 5) 6))) ; (1 2 3 4 5 6)
;; Using recursion
(defun flatten-array-recursive (arr)
(if (atom arr)
(list arr)
(mapcan #'flatten-array-recursive arr)))Split array into chunks using loop with by.
- Method:
(loop for i from 0 by size ...) - Subseq:
(subseq arr i (min (+ i size) ...)) - Use case: Batch processing
;; Chunk array
(defun chunk-array (arr size)
(loop for i from 0 below (length arr) by size
collect (subseq arr i (min (+ i size) (length arr)))))
(format t "~a" (chunk-array '(1 2 3 4 5 6) 2)) ; ((1 2) (3 4) (5 6))
;; Manual implementation
(defun chunk-array-manual (arr size)
(let ((result '()))
(do ((i 0 (+ i size)))
((>= i (length arr)))
(push (subseq arr i (min (+ i size) (length arr))) result))
(reverse result)))Binary search on sorted array using manual implementation.
- Manual: While loop with left/right pointers
- Time: O(log n)
- Requirement: Array must be sorted
;; Binary search
(defun binary-search (arr target)
(let ((left 0)
(right (1- (length arr))))
(loop while (<= left right)
do (let ((mid (floor (+ left right) 2)))
(cond
((= (nth mid arr) target) (return mid))
((< (nth mid arr) target) (setf left (1+ mid)))
(t (setf right (1- mid))))))
-1))
(format t "~a" (binary-search '(1 2 3 4 5 6 7) 5)) ; 4
;; Using built-in
(defun binary-search-built-in (arr target)
(position target arr :test #'<))Quick sort using pivot-based partitioning.
- Algorithm: Choose pivot, partition, recurse
- Time: O(n log n) average
- Implementation: Functional style
;; Quick sort
(defun quick-sort (arr)
(if (<= (length arr) 1)
arr
(let* ((pivot (car arr))
(rest (cdr arr))
(left (remove-if-not (lambda (x) (< x pivot)) rest))
(right (remove-if (lambda (x) (< x pivot)) rest)))
(append (quick-sort left) (list pivot) (quick-sort right)))))
(format t "~a" (quick-sort '(5 3 8 4 2 7 1 6)))Merge sort using divide-and-conquer.
- Algorithm: Divide, sort, merge
- Time: O(n log n)
- Space: O(n) auxiliary space
;; Merge sort
(defun merge-sort (arr)
(if (<= (length arr) 1)
arr
(let* ((mid (floor (length arr) 2))
(left (subseq arr 0 mid))
(right (subseq arr mid)))
(merge-lists (merge-sort left) (merge-sort right)))))
(defun merge-lists (left right)
(cond
((null left) right)
((null right) left)
((<= (car left) (car right))
(cons (car left) (merge-lists (cdr left) right)))
(t (cons (car right) (merge-lists left (cdr right))))))Bubble sort with early termination.
- Algorithm: Compare adjacent, swap
- Time: O(n²) worst case
- Optimization: Stop if no swaps
;; Bubble sort
(defun bubble-sort (arr)
(let ((sorted (copy-seq arr)))
(do ((i 0 (1+ i)))
((= i (1- (length sorted))))
(do ((j 0 (1+ j)))
((= j (- (length sorted) 1 i)))
(when (> (nth j sorted) (nth (1+ j) sorted))
(rotatef (nth j sorted) (nth (1+ j) sorted)))))
sorted))
;; Optimized bubble sort
(defun bubble-sort-optimized (arr)
(let ((sorted (copy-seq arr)))
(do ((i 0 (1+ i)))
((= i (1- (length sorted))))
(let ((swapped nil))
(do ((j 0 (1+ j)))
((= j (- (length sorted) 1 i)))
(when (> (nth j sorted) (nth (1+ j) sorted))
(rotatef (nth j sorted) (nth (1+ j) sorted))
(setf swapped t)))
(unless swapped (return sorted))))
sorted))Find common elements using remove-if-not or hash set.
- Filter:
(remove-if-not (lambda (x) (member x arr2)) arr1) - Set:
make-hash-table - Complexity: O(n²) or O(n)
;; Intersection of arrays
(defun intersection-arrays (arr1 arr2)
(remove-if-not (lambda (x) (member x arr2)) arr1))
(format t "~a" (intersection-arrays '(1 2 3 4) '(3 4 5 6))) ; (3 4)
;; Using set for efficiency
(defun intersection-set (arr1 arr2)
(let ((set2 (make-hash-table)))
(dolist (item arr2)
(setf (gethash item set2) t))
(remove-if-not (lambda (x) (gethash x set2)) arr1)))Combine arrays with unique elements using remove-duplicates.
- Method:
(remove-duplicates (append arr1 arr2)) - Set:
make-hash-table - Complexity: O(n log n)
;; Union of arrays
(defun union-arrays (arr1 arr2)
(remove-duplicates (append arr1 arr2)))
(format t "~a" (union-arrays '(1 2 3) '(3 4 5))) ; (1 2 3 4 5)
;; Using set
(defun union-set (arr1 arr2)
(remove-duplicates (append arr1 arr2)))Find elements in first array not in second.
- Difference:
(remove-if (lambda (x) (member x arr2)) arr1) - Symmetric:
appenddifferences - Set: Use hash set for efficiency
;; Difference of arrays
(defun difference-arrays (arr1 arr2)
(remove-if (lambda (x) (member x arr2)) arr1))
(format t "~a" (difference-arrays '(1 2 3 4) '(3 4 5 6))) ; (1 2)
;; Symmetric difference
(defun symmetric-difference (arr1 arr2)
(append (difference-arrays arr1 arr2)
(difference-arrays arr2 arr1)))Group objects by property using hash tables.
- Method:
make-hash-table - Grouping:
pushitems into groups - Complexity: O(n) time
;; Group by property
(defun group-by-property (items key)
(let ((groups (make-hash-table :test 'equal)))
(dolist (item items)
(let ((key-value
(if (equal key "type")
(type item)
(name item))))
(push item (gethash key-value groups))))
groups))
;; Usage
(defstruct item type name)
(defparameter *data*
(list (make-item :type "fruit" :name "apple")
(make-item :type "fruit" :name "banana")
(make-item :type "veg" :name "carrot")))
(defun print-groups (groups)
(maphash (lambda (key value)
(format t "~a: ~a" key value))
groups))Deep clone by recursively copying structures.
- Method:
condfor different types - Lists:
mapcar - Hash tables:
maphash
;; Deep clone object
(defun deep-clone (obj)
(cond
((null obj) nil)
((atom obj) obj)
((listp obj) (mapcar #'deep-clone obj))
((hash-table-p obj)
(let ((new (make-hash-table :test (hash-table-test obj))))
(maphash (lambda (key value)
(setf (gethash key new) (deep-clone value)))
obj)
new))
(t obj)))
;; Usage
(defstruct user name address)
(defstruct address city zip)
(defparameter *original*
(make-user :name "Alice"
:address (make-address :city "NYC" :zip "10001")))
(defparameter *cloned* (deep-clone *original*))Perform immutable updates on nested alists.
- Method:
aconsfor updates - Path: Dot notation for nested access
- Use case: Functional programming
;; Immutable update
(defun update-immutable (obj path value)
(let ((parts (split-string path ".")))
(if (= (length parts) 1)
(acons (car parts) value obj)
(let* ((first (car parts))
(rest (concatenate 'string (cdr parts) "."))
(nested (cdr (assoc first obj))))
(acons first
(update-immutable nested rest value)
obj)))))
(defparameter *state* '((user (name "Alice") (age 25))))
(defparameter *new-state* (update-immutable *state* "user.age" 26))Pipe composes functions from left to right.
- Method:
(pipe fns...) - Implementation:
dolistwithfuncall - Direction: Left to right
;; Pipe function
(defun pipe (&rest fns)
(lambda (value)
(let ((result value))
(dolist (fn fns)
(setf result (funcall fn result)))
result)))
(defun double (x) (* x 2))
(defun add-ten (x) (+ x 10))
(defun square (x) (* x x))
(defparameter *process* (pipe #'double #'add-ten #'square))
(format t "~a" (funcall *process* 5)) ; 400Compose functions from right to left.
- Method:
(compose fns...) - Implementation:
dolistwithfuncall - Direction: Right to left
;; Compose function
(defun compose (&rest fns)
(lambda (value)
(let ((result value))
(dolist (fn (reverse fns))
(setf result (funcall fn result)))
result)))
(defparameter *process2* (compose #'square #'add-ten #'double))
(format t "~a" (funcall *process2* 5)) ; 400Cache function results based on arguments.
- Method:
make-hash-table - Key: Arguments as key
- Trade-off: Memory for speed
;; Memoization
(defun memoize (fn)
(let ((cache (make-hash-table :test 'equal)))
(lambda (arg)
(multiple-value-bind (value found) (gethash arg cache)
(if found
value
(let ((result (funcall fn arg)))
(setf (gethash arg cache) result)
result))))))
(defvar *fibonacci-memo*
(memoize (lambda (n)
(if (<= n 1)
n
(+ (funcall *fibonacci-memo* (- n 1))
(funcall *fibonacci-memo* (- n 2)))))))
(format t "~a" (funcall *fibonacci-memo* 10))Ensure a function is called only once.
- Method: Use flag and closure
- Implementation: Track if called
- Use case: Initialization
;; Once function
(defun once (fn)
(let ((called nil)
(result nil))
(lambda ()
(unless called
(setf called t)
(setf result (funcall fn)))
result)))
(defparameter *initialize*
(once (lambda ()
(format t "Initialized")
'(id 1 name "App"))))
(funcall *initialize*) ; Prints "Initialized"
(funcall *initialize*) ; Returns cached resultDebounce with leading edge executes immediately then waits.
- Method: Track last call time
- Implementation:
last-calland timer - Use case: Save actions, API calls
;; Debounce with leading edge
(defun debounce-leading (delay-ms fn)
(let ((last-call 0)
(timer nil))
(lambda ()
(let ((now (/ (get-internal-real-time) 1000)))
(if (< (- now last-call) (/ delay-ms 1000))
(progn
(when timer
(sb-thread:terminate-thread timer))
(setf timer
(sb-thread:make-thread
(lambda ()
(sleep (/ delay-ms 1000))
(setf last-call (/ (get-internal-real-time) 1000))
(funcall fn)))))
(progn
(setf last-call now)
(funcall fn)))))))Throttle with leading edge executes at most once per time period.
- Method: Track last call time
- Implementation: Check time difference
- Use case: Scroll events
;; Throttle with leading edge
(defun throttle-leading (delay-ms fn)
(let ((last-call 0))
(lambda ()
(let ((now (/ (get-internal-real-time) 1000)))
(when (>= (- now last-call) (/ delay-ms 1000))
(setf last-call now)
(funcall fn))))))Deep equality comparison for nested structures.
- Method: Recursive comparison
- Lists:
every - Hash tables:
maphash
;; Deep equal
(defun deep-equal (obj1 obj2)
(cond
((eq obj1 obj2) t)
((or (null obj1) (null obj2)) nil)
((and (listp obj1) (listp obj2))
(and (= (length obj1) (length obj2))
(every #'deep-equal obj1 obj2)))
((and (hash-table-p obj1) (hash-table-p obj2))
(and (= (hash-table-count obj1) (hash-table-count obj2))
(maphash (lambda (key value)
(and (deep-equal value (gethash key obj2))
t))
obj1)))
(t (equal obj1 obj2))))Observable pattern for event notification.
- Observable: Maintains subscribers
- Subscribe: Add callback
- Notify: Call all subscribers
;; Observable pattern
(defclass observable ()
((subscribers :initform '() :accessor subscribers)))
(defgeneric subscribe (observable callback))
(defgeneric notify (observable data))
(defmethod subscribe ((obs observable) callback)
(push callback (subscribers obs))
(lambda () (setf (subscribers obs)
(remove callback (subscribers obs)))))
(defmethod notify ((obs observable) data)
(dolist (callback (subscribers obs))
(funcall callback data)))
;; Usage
(defparameter *obs* (make-instance 'observable))
(defparameter *unsubscribe*
(subscribe *obs* (lambda (data)
(format t "Received: ~a" data))))
(notify *obs* "Hello") ; Received: Hello
(funcall *unsubscribe*)
(notify *obs* "World") ; Nothing happensSingleton pattern using closure.
- Method: Closure with private state
- Thread-safe: Single-threaded
- Global access: Through variable
;; Singleton pattern
(defvar *singleton*
(let ((data (make-hash-table)))
(lambda (command &rest args)
(case command
(:set (setf (gethash (car args) data) (cadr args)))
(:get (gethash (car args) data))
(t nil)))))
;; Usage
(funcall *singleton* :set "name" "Alice")
(funcall *singleton* :get "name") ; AliceFactory pattern using functions.
- Method:
create-userfunction - Benefits: Decouples creation
- Classes: CLOS classes
;; Factory pattern
(defclass user () ())
(defclass admin (user)
((name :initarg :name :accessor name)))
(defclass guest (user)
((name :initarg :name :accessor name)))
(defclass regular-user (user)
((name :initarg :name :accessor name)))
(defun create-user (type name)
(case type
(:admin (make-instance 'admin :name name))
(:guest (make-instance 'guest :name name))
(t (make-instance 'regular-user :name name))))
;; Usage
(create-user :admin "Alice")Strategy pattern using generic functions.
- Interface:
defgeneric - Context: Uses strategy
- Benefits: Runtime switching
;; Strategy pattern
(defgeneric pay (strategy amount))
(defclass credit-card-strategy () ())
(defclass paypal-strategy () ())
(defclass crypto-strategy () ())
(defmethod pay ((strategy credit-card-strategy) amount)
(format t "Paid $~a with Credit Card" amount))
(defmethod pay ((strategy paypal-strategy) amount)
(format t "Paid $~a with PayPal" amount))
(defmethod pay ((strategy crypto-strategy) amount)
(format t "Paid $~a with Crypto" amount))
(defclass payment-context ()
((strategy :initarg :strategy :accessor strategy)))
(defgeneric execute-payment (context amount))
(defmethod execute-payment ((context payment-context) amount)
(pay (strategy context) amount))
;; Usage
(defparameter *context*
(make-instance 'payment-context :strategy (make-instance 'credit-card-strategy)))
(execute-payment *context* 100)
(setf (strategy *context*) (make-instance 'paypal-strategy))
(execute-payment *context* 50)Observer pattern using CLOS.
- Subject: Maintains observers
- Observer: Receives updates
- Benefits: Loose coupling
;; Observer pattern
(defclass observer ()
())
(defgeneric update (observer data))
(defclass subject ()
((observers :initform '() :accessor observers)
(state :initform "" :accessor state)))
(defmethod set-state ((subj subject) new-state)
(setf (state subj) new-state)
(notify-observers subj))
(defmethod attach ((subj subject) (obs observer))
(push obs (observers subj)))
(defmethod detach ((subj subject) (obs observer))
(setf (observers subj) (remove obs (observers subj))))
(defmethod notify-observers ((subj subject))
(dolist (obs (observers subj))
(update obs (state subj))))
(defclass concrete-observer ()
((name :initarg :name :accessor name)))
(defmethod update ((obs concrete-observer) data)
(format t "~a received: ~a" (name obs) data))
;; Usage
(defparameter *subject* (make-instance 'subject))
(defparameter *observer1* (make-instance 'concrete-observer :name "Observer1"))
(defparameter *observer2* (make-instance 'concrete-observer :name "Observer2"))
(attach *subject* *observer1*)
(attach *subject* *observer2*)
(set-state *subject* "Hello World")Decorator pattern using wrapper functions.
- Component: Base class
- Decorator: Wraps component
- Benefits: Flexible extension
;; Decorator pattern
(defclass coffee ()
((cost :initarg :cost :accessor cost)
(description :initarg :description :accessor description)))
(defun milk-decorator (coffee)
(make-instance 'coffee
:cost (+ (cost coffee) 2.0)
:description (concatenate 'string (description coffee) ", Milk")))
(defun sugar-decorator (coffee)
(make-instance 'coffee
:cost (+ (cost coffee) 1.0)
:description (concatenate 'string (description coffee) ", Sugar")))
;; Usage
(defparameter *coffee* (make-instance 'coffee :cost 5.0 :description "Coffee"))
(setf *coffee* (milk-decorator *coffee*))
(setf *coffee* (sugar-decorator *coffee*))
(format t "~a" (description *coffee*)) ; Coffee, Milk, Sugar
(format t "~a" (cost *coffee*)) ; 8.0Command pattern using classes.
- Command: Encapsulates request
- Invoker: Executes commands
- Benefits: Undo/redo
;; Command pattern
(defgeneric execute (command))
(defgeneric undo (command))
(defclass add-command ()
((receiver :initarg :receiver :accessor receiver)
(value :initarg :value :accessor value)))
(defmethod execute ((cmd add-command))
(push (value cmd) (receiver cmd)))
(defmethod undo ((cmd add-command))
(setf (receiver cmd)
(remove (value cmd) (receiver cmd))))
;; Usage
(defparameter *receiver* '(1 2 3))
(defparameter *cmd* (make-instance 'add-command :receiver *receiver* :value 4))
(execute *cmd*)
(format t "~a" *receiver*) ; (4 1 2 3)
(undo *cmd*)
(format t "~a" *receiver*) ; (1 2 3)Memento pattern for state restoration.
- Originator: Creates/restores mementos
- Memento: Stores state
- Caretaker: Manages mementos
;; Memento pattern
(defclass memento ()
((state :initarg :state :accessor state)))
(defclass originator ()
((state :initform "" :accessor state)))
(defmethod save-state ((obj originator))
(make-instance 'memento :state (state obj)))
(defmethod restore-state ((obj originator) (memento memento))
(setf (state obj) (state memento)))
(defclass caretaker ()
((mementos :initform '() :accessor mementos)))
(defmethod add-memento ((obj caretaker) memento)
(push memento (mementos obj)))
(defmethod get-memento ((obj caretaker) index)
(nth index (mementos obj)))
;; Usage
(defparameter *originator* (make-instance 'originator))
(defparameter *caretaker* (make-instance 'caretaker))
(setf (state *originator*) "State 1")
(add-memento *caretaker* (save-state *originator*))
(setf (state *originator*) "State 2")
(add-memento *caretaker* (save-state *originator*))
(setf (state *originator*) "State 3")
(restore-state *originator* (get-memento *caretaker* 0))
(format t "~a" (state *originator*)) ; State 1Mediator pattern for centralized communication.
- Mediator: Encapsulates communication
- Colleague: Communicates through mediator
- Benefits: Loose coupling
;; Mediator pattern
(defclass mediator ()
((colleagues :initform '() :accessor colleagues)))
(defmethod register ((med mediator) colleague)
(push colleague (colleagues med)))
(defmethod send-message ((med mediator) message sender)
(dolist (col (colleagues med))
(unless (eq col sender)
(receive-message col message))))
(defclass colleague ()
((name :initarg :name :accessor name)
(mediator :initarg :mediator :accessor mediator)))
(defmethod initialize-instance :after ((col colleague) &key)
(register (mediator col) col))
(defmethod send-message ((col colleague) message)
(send-message (mediator col) message col))
(defmethod receive-message ((col colleague) message)
(format t "~a received: ~a" (name col) message))
;; Usage
(defparameter *mediator* (make-instance 'mediator))
(defparameter *alice* (make-instance 'colleague :name "Alice" :mediator *mediator*))
(defparameter *bob* (make-instance 'colleague :name "Bob" :mediator *mediator*))
(send-message *alice* "Hello Bob!")Chain of Responsibility using classes.
- Handler: Processes or forwards
- Chain: Linked list of handlers
- Benefits: Decoupling
;; Chain of Responsibility
(defclass handler ()
((next-handler :initform nil :accessor next-handler)))
(defgeneric set-next (handler next))
(defgeneric handle (handler request))
(defmethod set-next ((handler handler) next)
(setf (next-handler handler) next)
next)
(defclass auth-handler (handler)
())
(defmethod handle ((handler auth-handler) request)
(if (getf request :token)
(progn
(format t "Authentication passed")
(let ((next (next-handler handler)))
(when next (handle next request))))
(format t "Authentication failed")))
(defclass logger-handler (handler)
())
(defmethod handle ((handler logger-handler) request)
(format t "Logging request: ~a" (getf request :url))
(let ((next (next-handler handler)))
(when next (handle next request))))
;; Usage
(defparameter *auth* (make-instance 'auth-handler))
(defparameter *logger* (make-instance 'logger-handler))
(set-next *auth* *logger*)
(handle *auth* '(:token "valid" :url "/api"))State pattern using classes.
- Context: Maintains state
- State: Defines behavior
- Benefits: Clean state management
;; State pattern
(defgeneric handle-state (state))
(defclass ready-state ()
())
(defclass processing-state ()
())
(defclass completed-state ()
())
(defmethod handle-state ((state ready-state))
(format t "Ready: Waiting for input"))
(defmethod handle-state ((state processing-state))
(format t "Processing: Working on task"))
(defmethod handle-state ((state completed-state))
(format t "Completed: Task finished"))
(defclass context ()
((state :initform (make-instance 'ready-state)
:accessor state)))
(defmethod request ((ctx context))
(handle-state (state ctx)))
;; Usage
(defparameter *context* (make-instance 'context))
(request *context*) ; Ready: Waiting for input
(setf (state *context*) (make-instance 'processing-state))
(request *context*) ; Processing: Working on task
(setf (state *context*) (make-instance 'completed-state))
(request *context*) ; Completed: Task finishedProxy pattern using classes.
- Subject: Real object
- Proxy: Controls access
- Benefits: Access control
;; Proxy pattern
(defclass real-subject ()
())
(defmethod request ((obj real-subject))
(format t "RealSubject: Handling request"))
(defclass proxy ()
((real-subject :initform nil :accessor real-subject)))
(defmethod request ((obj proxy))
(when (check-access obj)
(unless (real-subject obj)
(setf (real-subject obj) (make-instance 'real-subject)))
(request (real-subject obj))
(log-access obj)))
(defmethod check-access ((obj proxy))
(format t "Proxy: Checking access")
t)
(defmethod log-access ((obj proxy))
(format t "Proxy: Logging access"))
;; Usage
(defparameter *proxy* (make-instance 'proxy))
(request *proxy*)Flyweight pattern for sharing objects.
- Flyweight: Shared object
- Factory: Manages flyweights
- Benefits: Memory optimization
;; Flyweight pattern
(defclass flyweight ()
((shared-state :initarg :shared-state :accessor shared-state)))
(defmethod operation ((obj flyweight) unique-state)
(format t "Shared: ~a, Unique: ~a"
(shared-state obj) unique-state))
(defclass flyweight-factory ()
((flyweights :initform (make-hash-table :test 'equal)
:accessor flyweights)))
(defmethod get-flyweight ((factory flyweight-factory) shared-state)
(or (gethash shared-state (flyweights factory))
(setf (gethash shared-state (flyweights factory))
(make-instance 'flyweight :shared-state shared-state))))
;; Usage
(defparameter *factory* (make-instance 'flyweight-factory))
(defparameter *fw1* (get-flyweight *factory* "state1"))
(defparameter *fw2* (get-flyweight *factory* "state1"))
(defparameter *fw3* (get-flyweight *factory* "state2"))
(operation *fw1* "unique1")
(operation *fw2* "unique2")
(operation *fw3* "unique3")Bridge pattern using CLOS.
- Abstraction: High-level interface
- Implementation: Low-level operations
- Benefits: Separation of concerns
;; Bridge pattern
(defgeneric operation-impl (impl))
(defclass concrete-implementation-a ()
())
(defmethod operation-impl ((impl concrete-implementation-a))
(format t "ConcreteImplementationA: Operation"))
(defclass concrete-implementation-b ()
())
(defmethod operation-impl ((impl concrete-implementation-b))
(format t "ConcreteImplementationB: Operation"))
(defclass abstraction ()
((impl :initarg :impl :accessor impl)))
(defmethod operation ((abst abstraction))
(format t "Abstraction: Additional logic")
(operation-impl (impl abst)))
;; Usage
(defparameter *impl-a* (make-instance 'concrete-implementation-a))
(defparameter *impl-b* (make-instance 'concrete-implementation-b))
(defparameter *abstraction1* (make-instance 'abstraction :impl *impl-a*))
(defparameter *abstraction2* (make-instance 'abstraction :impl *impl-b*))
(operation *abstraction1*)
(operation *abstraction2*)Adapter pattern using wrapper classes.
- Target: Expected interface
- Adaptee: Existing interface
- Adapter: Bridges interfaces
;; Adapter pattern
(defclass target ()
())
(defmethod request ((obj target))
(format t "Target: Request"))
(defclass adaptee ()
())
(defmethod specific-request ((obj adaptee))
(format t "Adaptee: Specific Request"))
(defclass adapter ()
((adaptee :initarg :adaptee :accessor adaptee)))
(defmethod request ((obj adapter))
(specific-request (adaptee obj)))
;; Usage
(defparameter *adaptee* (make-instance 'adaptee))
(defparameter *adapter* (make-instance 'adapter :adaptee *adaptee*))
(request *adapter*)Facade pattern using composition.
- Facade: Simplified interface
- Subsystem: Complex components
- Benefits: Simplified interface
;; Facade pattern
(defclass subsystem-a ()
())
(defmethod operation-a ((obj subsystem-a))
(format t "SubsystemA: Operation"))
(defclass subsystem-b ()
())
(defmethod operation-b ((obj subsystem-b))
(format t "SubsystemB: Operation"))
(defclass facade ()
((subsystem-a :initform (make-instance 'subsystem-a) :accessor subsystem-a)
(subsystem-b :initform (make-instance 'subsystem-b) :accessor subsystem-b)))
(defmethod operation ((obj facade))
(operation-a (subsystem-a obj))
(operation-b (subsystem-b obj))
(format t "Facade: Complex operation"))
;; Usage
(defparameter *facade* (make-instance 'facade))
(operation *facade*)Composite pattern using classes.
- Component: Interface for all
- Leaf: Individual object
- Composite: Container
;; Composite pattern
(defgeneric operation (component))
(defclass leaf ()
((name :initarg :name :accessor name)))
(defmethod operation ((obj leaf))
(format t "Leaf ~a: Operation" (name obj)))
(defclass composite ()
((name :initarg :name :accessor name)
(children :initform '() :accessor children)))
(defmethod add ((obj composite) component)
(push component (children obj)))
(defmethod remove ((obj composite) component)
(setf (children obj) (remove component (children obj))))
(defmethod operation ((obj composite))
(format t "Composite ~a: Operation" (name obj))
(dolist (child (children obj))
(operation child)))
;; Usage
(defparameter *leaf1* (make-instance 'leaf :name "A"))
(defparameter *leaf2* (make-instance 'leaf :name "B"))
(defparameter *composite* (make-instance 'composite :name "Root"))
(add *composite* *leaf1*)
(add *composite* *leaf2*)
(operation *composite*)Visitor pattern using generic functions.
- Visitor: Defines operations
- Element: Accepts visitors
- Benefits: Adding operations without modifying
;; Visitor pattern
(defgeneric accept (element visitor))
(defgeneric visit (visitor element))
(defclass element-a ()
())
(defmethod accept ((obj element-a) visitor)
(visit visitor obj))
(defclass element-b ()
())
(defmethod accept ((obj element-b) visitor)
(visit visitor obj))
(defclass visitor ()
())
(defmethod visit ((visitor visitor) (element element-a))
(format t "Visiting ElementA"))
(defmethod visit ((visitor visitor) (element element-b))
(format t "Visiting ElementB"))
;; Usage
(defparameter *visitor* (make-instance 'visitor))
(defparameter *element-a* (make-instance 'element-a))
(defparameter *element-b* (make-instance 'element-b))
(accept *element-a* *visitor*)
(accept *element-b* *visitor*)Iterator pattern using classes.
- Iterator: Traverses collection
- Aggregate: Creates iterator
- Benefits: Uniform traversal
;; Iterator pattern
(defclass iterator ()
((collection :initarg :collection :accessor collection)
(index :initform 0 :accessor index)))
(defgeneric next (iterator))
(defgeneric has-next (iterator))
(defmethod next ((it iterator))
(if (has-next it)
(let ((result (nth (index it) (collection it))))
(incf (index it))
result)
nil))
(defmethod has-next ((it iterator))
(< (index it) (length (collection it))))
(defclass custom-collection ()
((items :initform '() :accessor items)))
(defmethod add ((col custom-collection) item)
(push item (items col)))
(defmethod get-iterator ((col custom-collection))
(make-instance 'iterator :collection (items col)))
;; Usage
(defparameter *collection* (make-instance 'custom-collection))
(add *collection* "A")
(add *collection* "B")
(add *collection* "C")
(defparameter *iterator* (get-iterator *collection*))
(loop while (has-next *iterator*)
do (format t "~a" (next *iterator*)))Template Method using generic functions.
- AbstractClass: Defines template
- ConcreteClass: Implements steps
- Benefits: Code reuse
;; Template Method pattern
(defclass abstract-class ()
())
(defgeneric template-method (obj))
(defgeneric step1 (obj))
(defgeneric step2 (obj))
(defgeneric step3 (obj))
(defmethod template-method ((obj abstract-class))
(step1 obj)
(step2 obj)
(step3 obj))
(defmethod step1 ((obj abstract-class))
(format t "Step 1"))
(defmethod step3 ((obj abstract-class))
(format t "Step 3"))
(defclass concrete-class (abstract-class)
())
(defmethod step2 ((obj concrete-class))
(format t "Concrete Step 2"))
;; Usage
(defparameter *concrete* (make-instance 'concrete-class))
(template-method *concrete*)Builder pattern using classes.
- Builder: Constructs parts
- Director: Orchestrates construction
- Product: Constructed object
;; Builder pattern
(defclass product ()
((parts :initform '() :accessor parts)))
(defmethod add-part ((obj product) part)
(push part (parts obj)))
(defmethod list-parts ((obj product))
(format t "~a" (parts obj)))
(defclass builder ()
((product :initform (make-instance 'product) :accessor product)))
(defmethod reset ((obj builder))
(setf (parts (product obj)) '()))
(defmethod build-step-a ((obj builder))
(add-part (product obj) "Part A"))
(defmethod build-step-b ((obj builder))
(add-part (product obj) "Part B"))
(defmethod get-result ((obj builder))
(product obj))
(defclass director ()
((builder :initarg :builder :accessor builder)))
(defmethod build-minimal ((obj director))
(build-step-a (builder obj)))
(defmethod build-full ((obj director))
(build-step-a (builder obj))
(build-step-b (builder obj)))
;; Usage
(defparameter *builder* (make-instance 'builder))
(defparameter *director* (make-instance 'director :builder *builder*))
(build-minimal *director*)
(defparameter *product* (get-result *builder*))
(list-parts *product*)Prototype pattern for cloning objects.
- Prototype: Cloneable object
- Clone: Creates a copy
- Benefits: Performance
;; Prototype pattern
(defclass prototype ()
((name :initarg :name :accessor name)
(nested :initarg :nested :accessor nested)))
(defmethod clone ((obj prototype))
(make-instance 'prototype
:name (name obj)
:nested (copy-list (nested obj))))
(defmethod deep-clone ((obj prototype))
(make-instance 'prototype
:name (name obj)
:nested (deep-copy (nested obj))))
(defun deep-copy (obj)
(cond
((null obj) nil)
((atom obj) obj)
((listp obj) (mapcar #'deep-copy obj))
((hash-table-p obj)
(let ((new (make-hash-table :test (hash-table-test obj))))
(maphash (lambda (key value)
(setf (gethash key new) (deep-copy value)))
obj)
new))
(t obj)))
;; Usage
(defparameter *original*
(make-instance 'prototype :name "Original" :nested '((value . 42))))
(defparameter *copy* (clone *original*))
(setf (name *copy*) "Copy")
(setf (cdr (assoc 'value (nested *copy*))) 99)
(format t "~a" (name *original*)) ; Original
(format t "~a" (cdr (assoc 'value (nested *original*)))) ; 42 (shallow copy)
(defparameter *deep-copy* (deep-clone *original*))
(setf (cdr (assoc 'value (nested *deep-copy*))) 100)
(format t "~a" (cdr (assoc 'value (nested *original*)))) ; 42 (deep copy)