Hopefully they’re useful!
This repo will contain examples (and exemplars) of what I think is useful lisp code. It will mainly be common lisp, but emacs lisp may also appear, and I guess any other lisp-y type languages (i.e. scheme).
The plan is that when I find something useful I will add it to this file in the Blog Ideas section and then, at some point in the future, I will write it up in a blog-post style. The blog posts will appear in my blog at https://stewart123579.github.io/blog/tags/clues
I write all of my posts (and almost everything else) using Emacs. In particular I use Org Mode, and I’ve recently discovered the useful ox-hugo minor mode to export the pages using the Hugo engine.
There are my various CLUEs (Common Lisp Useful Examples/Exemplars) posts.The orgmode (i.e. text) file that they are generated from is available: https://github.com/stewart123579/clues
I always forget how to update the list of local projects for Quicklisp.
(ql:register-local-projects)
I think I learned about it from Wimpie Nortje’s post.
There are two type of lisp coders. Those that love theloop
macro and those
that don’t.
I’m one of the former.
Take this beautiful construction:
(loop for item in mylist
collect (first item) into known-keys
maximize (second item) into big
minimize (second item) into small
finally (return (values known-keys big small)))
Even not knowing what mylist
was you could guess that known-keys
would be
the collection of all the first elements of item
, and big
and small
would
be the largest and smallest values in the second part of item
.
If I told you that mylist
was a list of items that had two elements, viz.
((a . 3) (b . 2) (c . 1))
You’d be able to guess that the result would be something like
(a b c)
3
1
loop
is easy to read and reflects the true strength of common lisp - DSLs make
readable (and performant) code easy.
Here’s how to remove variables and functions from an image:
(makunbound 'variable)
(fmakunbound 'function)
So, for example:
(defvar foo 42)
(defun bar () (print "Hello world!"))
(boundp 'foo) ;; T
(fboundp 'bar) ;; T
(makunbound 'foo)
(fmakunbound 'bar)
(boundp 'foo) ;; nil
(fboundp 'bar) ;; nil
Read on to find out why I prefer the lisp approach over most other languages.
In interpreted languages, like python and R there is the concept of removing or deleting a variable or function.
In python you del variable
, in R you rm(variable)
:
foo = 5
foo
Naturally results in
Now we delete the variable:
del foo # Delete foo
foo
# Traceback (most recent call last): # File "<stdin>", line 1, in <module> # NameError: name 'foo' is not defined
Why do we use makunbound
and fmakunbound
, etc., in common lisp?
(The following is lightly rewritten and sourced from Peter Norvig’s Paradigms of Artificial Intelligence Programming)
…because common Lisp has at least seven name spaces. The two we think of most often are
- Functions and Macros
- Variables
Python, R, Scheme, etc. conflate these two name spaces, but Common Lisp keeps
them separate, so that in a function application like (f)
the function/macro
name space is consulted for the value of f
, but in (+ f)
, f
is treated as
a variable name.
- Special variables form a distinct name space from lexical
variables
@@hugo:{{< line-break >}}@@
So the
f
in(+ f)
is treated as either a special or lexical variable, depending on if there is an applicable special declaration. - Data types
@@hugo:{{< line-break >}}@@
Even if
f
is defined as a function and/or a variable, it can also be defined as a data type withdefstruct
,deftype
, ordefclass
. - Labels for
go
statements within a tagbody - Block names for
return-from
statements within a block - Symbols inside a quoted expression are treated as constants, and thus form name space @@hugo:{{< line-break >}}@@ These symbols are often used as keys in user-defined tables, and in a sense each such table defines a new name space. One example is the tag name space, used by catch and throw. Another is the package name space.
It is a good idea to limit each symbol to only one name space. Common Lisp will not be confused if a symbol is used in multiple ways, but the poor human reader probably will be.
- Peter Norvig, Paradigms of Artificial Intelligence Programming
🤣
It cuts down on errors.When collaborating it is easy to clash in your namespaces. You might define the
variable two_pi
, I might want to define two_pi
as a function. My code runs,
I merge it. Your code runs, you merge it. We didn’t have appropriate testing…
💣💥
Here’s a contrived example, presented in three different programming languages: R, python and (common) lisp.
Here I define a variable two_pi
to be twice the value of \( π \), then I
define a function to calculate the circumference of a circle and then I define a
function (also) called two_pi
. Let’s see what happens…
two_pi <- 2 * pi
circumference <- function(r) {
cat("R: The circumference is ", (two_pi * r), "\n")
}
# We all know pi is actually 3
two_pi <- function() {
return(6);
}
circumference(1)
In this case R redefines what is meant by two_pi
and the circumference function
fails. Why? Because you need to call functions: two_pi()
Error in two_pi * r : non-numeric argument to binary operator
import math
two_pi = 2 * pi
def circumference(r):
print(f"python: The circumference is {two_pi * r}")
# We all know pi is actually 3
def two_pi():
return 6
circumference(1)
Python also redefines what is meant by two_pi
and the circumference function fails.
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in circumference TypeError: unsupported operand type(s) for *: 'function' and 'int'
(setf two_pi (* 2 pi))
(defun circumference (r)
(format t "cl: The circumference is ~a~&" (* two_pi r)))
;; We all know pi is actually only 3.
(defun two_pi ()
6)
(circumference 1)
Common lisp works, because it keeps functions and variables in difference namespaces and knows which one to call at which point.
cl: The circumference is 6.283185307179586d0