- Published on
learning COME ON lisp 2025
learning lisp in 2025
so you want to learn lisp?me too.lisp was so influencial that even fundamentally new computer architecture was built to program in lisp called lisp machines. lets learn together this awesome language to get started with any language we need compiler when i searched about it i got clasp and sbcl for common lisp note that there are many languages of lisp family scheme,raku,racket etc.. you can explore yourself, i also got lem ide but as i belong to neovim cult i see no going for an 'IDE' ,so i decided to go with sbcl for now because i asked my one and only best friend of college and life Mr. Chatgpt and he said go for sbcl.As for installing sbcl, i am using arch it's just
sudo pacman -S sbcl
you can get help online for other distro or OS or even compile the compiler itself .Anyway we are using lisp because of its awesome and powerful macro system we are programmer not coders so we will program the way we program with the programming language which has a flexible macro system to lets you invent the way you program. what ahead?do
sbcl --help
we begin our journey with a great quote from death note as great light yagami told us
(defun light () (print "the world is rotten"))
(light)
defun is used to create a function,it is the standard way to define a named global function first lines is a function while second line is function call ,of but not thats how you see lisp programs on all over internet they are often like this
(defun light ()
(print "the world is rotten"))
(light)
but i think first one is more clear as a begginner,we can also define anonymous functions in lisp,
(funcall (lambda () (print "Are you going to kill me")))
anonymous functions also exits in other major functional languages as haskell,rust etc.., they are useful when you dont wanna create a global function or want to pass functions as argument
(apply (lambda (name)
(format t "The world is rotten.~% ~a! " name))
'("Light Yagami"))
you can achieve the same functionality with funcall without using apply. The key difference is that with funcall, you pass all the arguments individually, whereas apply requires the arguments to be bundled in a list
- The lambda function now takes one parameter, name
(funcall (lambda (subject action)
(format t "~a ~a is crime.~%" subject action))
"Catching"
"Kira")
(apply (lambda (subject action)
(format t "~a ~a is crime.~%" subject action))
'("Catching" "Kira"))
the labels
construct is used to define local recursive functions. It allows you to define functions inside other functions (i.e., nested functions), and these local functions can call themselves recursively or call each other. This is especially useful when you want to encapsulate a helper function that needs to be recursive, without exposing it globally.
labels
:
Syntax of (labels ((function-name1 (parameters) body1)
(function-name2 (parameters) body2)
...)
body)
- function-name: The name of the function being defined.
- parameters: The parameters that the function will take.
- body: The code that will execute when the function is called.
- body (after
labels
): The code that runs when the outer function is invoked, typically using the local functions defined insidelabels
.
Use Case: Local Recursive Helper Functions
labels
Example: Factorial Using (defun factorial (n)
(labels ((rec (x) ; local recursive function
(if (zerop x)
1
(* x (rec (- x 1))))) ; recursive call to rec
(rec n))) ; start recursion with n
- Inside
factorial
, we define a local recursive functionrec
usinglabels
.rec
takesx
and computesx * (rec (- x 1))
, and whenx
is zero, it returns 1 (base case of the recursion).
Use Case: Mutually Recursive Functions
labels
can also be used for mutual recursion, where two or more functions call each other.
Example: Odd/Even Checking with Mutual Recursion
(defun odd-or-even (n)
(labels ((even? (x)
(if (zerop x)
t
(odd? (- x 1)))) ; calls odd? when even? gets an odd number
(odd? (x)
(if (zerop x)
nil
(even? (- x 1))))) ; calls even? when odd? gets an even number
(even? n))) ; start the check with even?
Explanation:
- The
odd-or-even
function useslabels
to define two local functions,even?
andodd?
. - Mutual Recursion:
even?
callsodd?
when the number is not zero.odd?
callseven?
when the number is not zero.
You could call (odd-or-even 5)
and it would return nil
(false for even), and (odd-or-even 6)
would return t
(true for even).
labels
?
Why Use - Encapsulation: The local recursive functions defined inside
labels
are not visible outside the function in which they are defined, helping keep your code modular and free from unnecessary global functions. - Recursive Functions:
labels
is especially useful when you need recursion, allowing you to define recursive functions inline without worrying about global scope pollution. - Mutual Recursion:
labels
allows you to define mutually recursive functions that can call each other, which is common in certain types of problems (like checking if a number is odd or even).
labels
: flet
Alternative to In older versions of Lisp, flet
was commonly used to define local functions, but labels
is more powerful because it allows mutual recursion. If you don’t need recursion, flet
can be an alternative to labels
.
flet
:
Example of Using (defun factorial (n)
(flet ((rec (x)
(if (zerop x)
1
(* x (rec (- x 1)))))) ; recursive call to rec
(rec n)))
In this example, flet
defines a local function rec
that doesn't have the capability of mutual recursion, so it’s only suitable for simpler, non-recursive tasks.
Clousres
closures are created when a function returns another function that captures its lexical environment
(defun crime-statement ()
(let ((years-passed 6)
(crime-reduction 70)
(name "KIRA"))
(lambda ()
(format t "It's been ~A years since ~A's first appearance.~%" years-passed name)
(format t "Worldwide crime has been reduced by ~A%.~%" crime-reduction))))
(let ((crime-update (crime-statement)))
(funcall crime-update))
crime-statement creates a closure. Inside it, we define two local variables (years-passed and crime-reduction), and return a lambda function that references those variables
(if '(L Mello Near)
'the-list-has-suspects
'the-list-is-empty)
;; Returns: 'the-list-has-suspects
(if '()
'the-list-has-suspects
'the-list-is-empty)
;; Returns: 'the-list-is-empty
(defun my-length (list)
(if list
(1+ (my-length (cdr list)))
0))
(my-length '(L Mello Near))
;; Returns: 3
(if (= (+ 1 2) 3)
'prepare-alibi
'stay-calm)
;; Returns: 'prepare-alibi
(defvar *justice-prevails* nil)
(defun light-strategy (x)
(if (oddp x)
(progn
(setf *justice-prevails* t)
'kira-wins)
'kira-loses))
(light-strategy 5)
*justice-prevails*
;; Returns: 'kira-wins and *justice-prevails* is t
(defvar *kira-dominates* nil)
(when (oddp 5)
(setf *kira-dominates* t)
'plan-executed)
;; Returns: 'plan-executed and *kira-dominates* is t
(defvar *rival* nil)
(defun handle-challenge (person)
(cond ((eq person 'L) (setf *rival* 'genius-detective)
'(I must eliminate L at all costs))
((eq person 'Near) (setf *rival* 'cold-strategist)
'(Near is dangerous, but predictable))
(t '(I will crush anyone in my way))))
(handle-challenge 'L)
*rival*
;; Returns: '(I must eliminate L at all costs) and *rival* is 'genius-detective
(if (member 'Near '(L Mello Near))
'near-is-no-problem
'we-have-nobody)
;; Returns: 'near-is-a-threat
ok so i think i am liking lisp now next step is to fire up emacs and configuring it may be i'll do it and may be i dont , well i dont know ,well writing lisp doesnt give feel of functional programming ,it is very common practice to do side effect in lisp also functions in lisp are not pure in any way they can store state they can change values over other memory such as global variable,i think writing lisp is more or like fun on its own way but i will continue to explore it , i feel like programming in lisp is like abstraction over absractions and procedures over procedures .
(defstruct person
name age favorite-color)
(defgeneric process-data (data)
(:documentation "A generic function to handle various data types."))
(defmethod process-data ((data array))
(format t "Array length: ~D~%" (length data)))
(defmethod process-data ((data hash-table))
(format t "Hash-table keys: ~A~%" (mapcar #'car (loop for k being the hash-keys of data collect (list k)))))
(defmethod process-data ((data list))
(format t "List contents: ~A~%" data))
(defmethod process-data ((data person))
(format t "Person's name: ~A, Age: ~D, Favorite Color: ~A~%"
(person-name data) (person-age data) (person-favorite-color data)))
(defun demo-advanced-datatypes ()
"Demonstrates arrays, hash tables, and structures."
(let ((arr (make-array 3 :initial-element 0))
(ht (make-hash-table))
(p (make-person :name "Alice" :age 30 :favorite-color "Green")))
;; Array example
(setf (aref arr 1) 'foo)
(format t "Array example: ~A~%" arr)
;; Hash table example
(setf (gethash 'key1 ht) 'value1
(gethash 'key2 ht) 'value2)
(format t "Hash table example: ~A~%" (loop for k being the hash-keys of ht collect (list k (gethash k ht))))
;; Structure example
(setf (person-age p) 31)
(format t "Structure example: ~A~%" p)
;; Generic function demonstration
(mapcar #'process-data (list arr ht '(a b c) p))))
(defun demo-sequence-functions ()
"Demonstrates sequence functions and generic handling."
(let ((list-data '(1 2 3 4 5))
(string-data "hello world"))
(format t "Reduce example (sum): ~D~%" (reduce #'+ list-data))
(format t "Find-if example: ~A~%" (find-if #'oddp list-data))
(format t "Some example: ~A~%" (some #'oddp list-data))
(format t "Count example: ~D~%" (count #\l string-data))
(format t "Position example: ~D~%" (position #\w string-data))
(format t "Every example: ~A~%" (every #'numberp list-data))))
(defun demo-generic-functions ()
"Demonstrates defining and using generic methods."
(defmethod add ((a number) (b number)) (+ a b))
(defmethod add ((a list) (b list)) (append a b))
(format t "Add numbers: ~D~%" (add 5 10))
(format t "Add lists: ~A~%" (add '(1 2) '(3 4))))
(defun run-all-examples ()
"Runs all demonstrations."
(format t "=== Advanced Datatypes ===~%")
(demo-advanced-datatypes)
(format t "~%=== Sequence Functions ===~%")
(demo-sequence-functions)
(format t "~%=== Generic Functions ===~%")
(demo-generic-functions))
(run-all-examples)
well i think we are familiar with some of lisp now but we havent explored amazing macro system of lisp and many more thing we will do it later while making game because writing these short programs is only good for initial 100 lines of writing any language then you learn by making something that brings joy .