PicoLisp Explored: The SET function

Search for a command to run...

Brilliant! Glad you wrote about this. 👏
In this series, some more advanced features of PicoLisp will be explained that have not been covered in the beginner's tutorial.
The Dining Philosophers
In our previous post, we introduced how to utilize Discrete Event Simulation (DES) in PicoLisp. Now, let's explore another application: a railroad model simulation that includes tracks and trains. The visualization is ASCII-based, so feel free to unl...

The Dining Philosophers

In this post, we will introduce the concept of co-routines and how they are handled in PicoLisp. This will be the foundation for the subsequent posts on Discrete Event Simulation and finally a couple of examples, including an ASCII model railway simu...

One of the many nice things of PicoLisp is that it comes with a built-in database. This makes it easy to collect and store our own data, for example via a mobile app. But not all data is best represented in tables and lists: sometimes a colorful litt...

In the last two posts, we have built a little app that shows the current location and displays the nearest ice cream shops: https://picolisp-explored.com/an-openstreetmap-app-written-in-picolisp-pt-1 https://picolisp-explored.com/an-openstreetmap-ap...

In this series, we will further explore functions and concepts of PicoLisp that have not been covered in the beginner's tutorial.
Today we will talk about the set function. Although its purpose is simple (setting values to variables), the usage can be confusing if you are not aware of the internal data representation in PicoLisp. So let's go through it and try to understand what is going on.
Some knowledge of the internal representation of data will be helpful, so consider to read the article about concepts and data types first if you haven't done so.
set functionFrom the docs:
(set 'var 'any ..) -> anyStores new values
anyin thevararguments.
Examples:
: (set 'L '(A B C) (cdr L) 999)
-> 999
: L
-> (A 999 C)
Let's take a look at the example: First we set (A B C) to the L symbol, and then we write 999 in its cdr. We know that (cdr L) is (B C).
Question: Why do we get (A 999 C) as result, and not (A 999) (as one might expect)?
Let's define L as (A B C) and then set L to 999.
:(setq L '(A B C))
-> (A B C)
: (set L 999)
-> 999
What do you expect as result?
: L
-> (999 B C)
Only the first item in the list L is changed. Now the cdr:
:(cdr L)
-> (B C)
:(set (cdr L) 888)
-> 888
Again, what do you expect? This is the result:
: L
-> (999 888 C)
Only the first item of the cdr is changed.
Now let's see what happens if we modify the car of a list of symbols: '(A B C).
:(setq L '(A B C))
-> (A B C)
:(set (car L) 999)
-> 999
: L
-> (A B C)
The first item of L is still A! Why? Because the symbol A has changed, not L:
: A
-> 999
Obviously, there is a difference between (set (car L) 999) and (set L 999). But what exactly is different?
Maybe you already see the point. In any case, let's go back to the basics to really understand what is happening here.
We remember: PicoLisp consists only of "cells" where each cell has a CAR and a CDR.
+-----+-----+
| CAR | CDR |
+-----+-----+
A symbol has a value VAL, which is in the CDR of the symbol. The pointer to the symbol points at the CDR part of the cell.
Symbol
|
V
+-----+-----+
| / | VAL |
+-----+-----+
Now let's have a look at lists. Lists are constructs that have the value in the CAR and a pointer to the next cell in the CDR.
|
V
+-----+-----+
| any | | |
+-----+--+--+
|
V
+-----+-----+
| any | | |
+-----+--+--+
|
V
...
The important thing to understand is that set takes the new value and writes it to the "position of the arrow".
It is easier to understand if we directly see the cell structure. Let's re-visit the very first example.
:(setq L '(A B C))
: (set L 999)
: L
-> (999 B C)
L is a list, which means that the pointer points at the CAR. So what is actually set, is this:

The rest of the list is the same, especially the pointers to the next cell.
Now back to the second example, where we set a new value to (cdr L). (cdr L) is also a list, where its first element is B. B actually is a symbol which is stored in its own cell, so the car of the first list contains a pointer to the B cells. So if we now execute:
:(set (cdr L) 888)
-> 888
then the pointer to the B-cell is overwritten by 888. Again, the pointer to the next cell is unchanged:

Now let's revisit the third example from above:
:(setq A 7 B 2 C 3)
-> 7
:(setq L '(A B C))
-> (A B C)
The items(A B C) are each symbols which have some value assigned. Their value is not directly stored in the cell (as would be the case for numbers), but in a separate cell. This means that (car L) actually contains a pointer to the symbol A.
Let's consider a symbol A with value 7. Then the cells will look like this:
|
V
+-----+-----+
A | | | 7 |
+--+--+-----+
|
V
+-----+-----+
| | | 65 | ASCII "A" = 65
+--+--+-----+
The pointer from the list is pointing to the CDR, which contains the value of A. Now let's execute:
:(set (car L) 111)
-> 111
Which value are we actually setting? Well, this one:

Since we only change the value of A, the structure of L and A remain unchanged. Lstill points to A, only that it now has a different value.
What happens if we try to do (set (car L) 111) if the first item of L is not a symbol?
:(setq L '(1 2 3)
!? (set (car L) 111)
1 -- Variable expected
We get an error becaus integers are stored directly in the cell. Therefore there is nocar which could be addressed.
As you can see, even a simple function such as set is much easier to understand with some knowledge of the internal cell representation in PicoLisp.
We will see many examples of the usage of set in the Rosetta Code examples as it is a widely used function, for example in the next post about the 100 Doors riddle.
Sources:
https://software-lab.de/doc/index.html
https://software-lab.de/doc/ref.html