8. Variables

As in other languages, we have local and global variables. However, the scope of variables in Common Lisp is very important and has particularities that you must know.

Local variables

They are defined with the let or let* function, and are only accessible within the block where they are defined.

(let ((x 10)
      (y 20))
  (+ x y)) ; 30

If I try to perform the same arithmetic operation outside the let block, I will get an error:

(let ((x 10)
      (y 20)))

(+ x y) ; Error: Variable X is unbound.

No matter how deep your code is, the variables will be accessible as long as they are inside the let block.

However, you should know that variables do not respect the order in which you define them. For example:

(let ((x 1)
       (y 2)
       (z (+ x y))) ; Error: Variable X is unbound.
    z)

You get an error because x and y are not defined at the moment z is calculated. The solution is to evaluate them, and for that there is let*:

(let* ((x 1)
       (y 2)
       (z (+ x y))) ; Now it works correctly.
    z) ; 3

The difference between let and let* is that let evaluates all the variable initializations in parallel, while let* evaluates them sequentially, allowing previously defined variables to be used in later initializations.

When should I use each one? let by default since it is more efficient, and let* only when you need one variable to depend on another.

Dynamic variables

We could consider them global variables, but with a scope that is dynamic.

They are defined with defparameter or defvar.

(defparameter *mi-variable* 42)

(defvar *otra-variable* "Hola, mundo!")

Their differences are subtle but important.

defparameter always initializes the variable, even if it already exists. defvar only initializes the variable if it does not already exist.

For example, if you run:

(defparameter *mi-variable* 100)
*mi-variable* ; 100

(defparameter *mi-variable* 200)
*mi-variable* ; 200
The variable *mi-variable* is 200 because defparameter has redefined it.

(defvar *otra-variable* "Hola mundo!")
(defvar *otra-variable* "Nos vemos luego!")
*otra-variable* ; "Hola mundo!"
The variable *otra-variable* still holds "Hola mundo!" because it was already defined. To overwrite it you must use setf:

(setf *otra-variable* "Nos vemos luego!")
*otra-variable* ; "Nos vemos luego!"

A good practice is not to change the values of dynamic variables unless it is strictly necessary, since it can lead to unexpected behavior in the code. Instead, instantiate local variables inside functions or let blocks.

(defparameter *impuesto* 21)

(defun calcular-precio-con-impuesto-reducido (precio)
  (let ((*impuesto* 10)) ; Local variable
    (* precio (+ 1 (/ *impuesto* 100)))))
Building SPAs with Django and HTML Over the Wire: Learn to build real-time single page applications with Python

Building SPAs with Django and HTML Over the Wire: Learn to build real-time single page applications with Python

The HTML over WebSockets approach simplifies single-page application (SPA) development and lets you bypass learning a JavaScript rendering framework such as React, Vue, or Angular, moving the logic to Python. This web application development book provides you with all the Django tools you need to simplify your developments with real-time results.

Buy the book

Help me keep writing

Every coffee gives me a push toward the next article.

Comments

There are no comments yet.