DEV Community

Cover image for PicoLisp Explored: The SET function
Mia
Mia

Posted on • Originally published at picolisp-blog.hashnode.dev

PicoLisp Explored: The SET function

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.


The set function

From the docs:

(set 'var 'any ..) -> any

Stores new values any in the var arguments.

Examples:

    : (set 'L '(A B C)  (cdr L) 999)
    -> 999
    : L
    -> (A 999 C)
Enter fullscreen mode Exit fullscreen mode

Teaser: Playing around in the REPL

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
Enter fullscreen mode Exit fullscreen mode

What do you expect as result?

: L
-> (999 B C)
Enter fullscreen mode Exit fullscreen mode

Only the first item in the list L is changed. Now the cdr:

:(cdr L)
-> (B C)
:(set (cdr L) 888)
-> 888
Enter fullscreen mode Exit fullscreen mode

Again, what do you expect? This is the result:

: L
-> (999 888 C)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

The first item of L is still A! Why? Because the symbol A has changed, not L:

: A
-> 999
Enter fullscreen mode Exit fullscreen mode

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.


Concepts and Data Types revisited

We remember: PicoLisp consists only of "cells" where each cell has a CAR and a CDR.

         +-----+-----+
         | CAR | CDR |
         +-----+-----+
Enter fullscreen mode Exit fullscreen mode

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 |
      +-----+-----+
Enter fullscreen mode Exit fullscreen mode

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
                        ...
Enter fullscreen mode Exit fullscreen mode

The important thing to understand is that set takes the new value and writes it to the "position of the arrow".


Visualizing the cells

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)
Enter fullscreen mode Exit fullscreen mode

L is a list, which means that the pointer points at the CAR. So what is actually set, is this:

thishaschanged.png

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
Enter fullscreen mode Exit fullscreen mode

then the pointer to the B-cell is overwritten by 888. Again, the pointer to the next cell is unchanged:

cdrchanged.png


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)
Enter fullscreen mode Exit fullscreen mode

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
         +--+--+-----+
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Which value are we actually setting? Well, this one:

carL.png

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
Enter fullscreen mode Exit fullscreen mode

We get an error becaus integers are stored directly in the cell. Therefore there is nocar which could be addressed.


Wrap-up

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:

Top comments (0)