A very first PicoLisp program
Tags: picolisp, lisp, functional, tutorial
Published: 2021-11-03 08:19:08

To conclude our "PicoLisp for Beginners" Series, we should talk about naming conventions, and finally create our first own little program!

-----------------------------

### Naming Conventions

PicoLisp has a few naming conventions that should be followed, in order to introduce name conflicts and keep the code readable for other programmers. Here are some of the most important ones:

- Global variables start with an asterisk "*"
- Global constants may be written all-uppercase
- Functions and other global symbols start with a lower case letter
- Locally bound symbols start with an upper case letter (for example, arguments in a function declaration)

**Note:** This list is not exhaustive, since some concepts were left out that we did not talk about yet (for example, classes). For the complete list of conventions refer to https://software-lab.de/doc/ref.html#conv.

----------------

### What do you mean by "name conflicts"?

For example, let's take the built-in PicoLisp function ``car``. Since data and functions are basically the same thing (see "Concepts and Data Types of PicoLisp"), a local variable "car" could overshadow the function definition of ``car``:

```
: (de max-speed (car)
   (.. (get car 'speeds) ..) )
-> max-speed
```

Inside the body of ``max-speed`` (and all other functions called during that execution) the kernel function ``car`` is redefined to some other value, and will surely crash if something like ``(car Lst)`` is executed. Instead, it is safe to write:

```
: (de max-speed (Car)            # 'Car' with upper case first letter
   (.. (get Car 'speeds) ..) )
-> max-speed
```
Another example of a name conflict: The symbols T and NIL are global constants, so care should be taken not to bind them to some other value by mistake:

```
(de foo (R S T)
   ...
```

-----------------------

### Scripting

Obviously it is hard to only rely on the REPL if the programs get more complex. Let's now write a minimal program to illustrate how a basic PicoLisp program should look like: a little "greeting"-program that asks the user for a name and prints it.

----------------

**For best learning results, consider to code along while reading. If you want to skip the explanations, you can find the link to the final scripts at the end.**

----------------

As you might remember from the Input-Output section of the "60 basic functions" series, we can read in a line using the ``line`` function. Let's try to store it in a variable called "Name". We use the REPL to test our idea:

```
: (setq Name (line))
Mia
-> ("M" "i" "a")
```
Let's check it:

```
: (prinl "Hello " Name)
Hello Mia
```

Okay, this seems to work. So, as second step, let's open a text file called **greeting.l** (.l is the ending for PicoLisp scripts) and paste the two lines inside.

```
# greeting.l

(prinl "Hello! Who are you?")
(setq Name (line))
(prinl "Hello " Name "!")
```

We can execute the script using the interpreter by typing ``pil greeting.l`` from the console. However, unfortunately our output looks kind of strange.

```
$ pil greeting.l
Hello! Who are you?                                                              
Hello !  
```
*[The $ symbolizes a shell command and doesn't need to be typed.]*

It seems our ``(line)`` command was skipped! Why is that? 

According to the [documentation](https://software-lab.de/doc/index.html), ``line`` "*reads a line of characters from the current input channel*". It seems we need to specify that we want to read from the **console**, because during the execution of a file, the current "input channel" is the **file** itself. 

After searching the documentation, we find that we can define that by the function ``in NIL`` (as opposite to ``in <filename>``, to read in from a file).

So we change it and the modified script looks like this:

```
# greeting.l

(prinl "Hello! Who are you?")
(setq name (in NIL (line)))
(prinl "Hello " name "!")
```

After calling it:
```
Hello! Who are you?
Mia                                                              
Hello Mia!  
:
```

It works! After execution the REPL is still open, as we can see by the colon in the next line. Therefore we add ``(bye)`` as last line of the function to close it.

----------------

Now, for the sake of demonstration, let's give the greeting its own function. We remember that a function is defined using the ``de`` function.

```
(de greeting (Name)
   (prinl "Hello " Name "!") )
```
*The function argument "Name" is spelled with an uppercase letter according to the conventions that we saw above. Also, note that the parentheses in the second line have some space in between: ``) )``. This is to visualize that the first bracket is closing the bracket from the same line, but the second one comes from the line above.*

Of course, the ``greeting`` function needs to be defined **before** it gets called, otherwise the interpreter will not know what to do with it. The complete program now looks like this:
```
# execute this script by calling "pil greeting.l"

(de greeting (Name)
  (prinl "Hello " Name "!") )

(prinl "Hello! Who are you?")
(setq Name (in NIL (line)))
(greeting Name)
(bye)
```

The complete example can be download it [here](https://gitlab.com/picolisp-blog/single-plage-scripts/-/blob/main/beginners-tutorial/greeting-script.l).

------------------------

### Creating an executable program

It can be a little bit unconvenient to use the interpreter for calling the program, especially if it gets more complex. Let's check the [tutorial](https://software-lab.de/doc/tut.html) how to create an executable file:

> It is better to write a single executable file using the mechanisms of "interpreter files". If the first two characters in an executable file are "#!", the operating system kernel will pass this file to an interpreter program whose pathname is given in the first line (optionally followed by a single argument). This is fast and efficient, because the overhead of a subshell is avoided. 

1. Step 1: **Find the path to the PicoLisp interpreter.**

    If PicoLisp is installed **globally**, for example using a package manager, it is most probably in the folder ``/usr/bin``. We could verify this using the shell command ``which``:

    ```
$ which picolisp
/usr/bin/picolisp
```

    If the program is installed only **locally**, the path to the "picolisp" interpreter will be in the ``bin/`` folder of the installation folder: ``<Path to Folder>/pil21/lib``.  We can find the path using the shell command ``locate``:

    ```
$ locate "bin/picolisp"
/home/user/pil21/bin/picolisp
```

2. Step 2: **Add the library file ``lib.l``**

    The interpreter alone is not enough, we also need the library called ``lib.l``.  For **global installations**, we will usually find it in the ``/usr/lib`` folder.
    ```
$ locate "picolisp/lib.l"
/usr/lib/picolisp/lib.l
```
    For **local installations**, it is in the root of the installation folder.
    ```
$ locate "lib.l"
/home/user/pil21/lib.l
```

    Fine! Now back to our script!

3. Step 3: **Adding the path to the interpreter and the library**: This should be the first line in your script.

    ```
#! /usr/bin/picolisp /usr/lib/picolisp/lib.l
```
    Replace these folders by your local folders if PicoLisp is not installed globally. Alternatively, you can also set soft links to ``/usr/bin`` and ``/usr/lib``:

    ```
$ ln -s /home/foo/pil21 /usr/lib/picolisp
$ ln -s /usr/lib/picolisp/bin/picolisp /usr/bin
```

4. Step 4: **Making the script executable**

    As a last step, let's make our script executable by ``chmod +x greetings.l``. Now we can execute the script by ``$ ./greeting.l``:

    ```
$ ./greeting.l 
Hello World! Who are you? 
Mia
Hello Mia!
```

That's it! The final script can be downloaded [here](https://gitlab.com/picolisp-blog/single-plage-scripts/-/blob/main/beginners-tutorial/greeting-executable.l). 

-----------------------------

### Adding arguments 

Sometimes we want to add the arguments directly at the execution of the script, instead of typing it after it started.

To do this, we modify our ``greeting`` function so that it prints a global variable called``*Name`` (according to the convention that global variables should start with an asterisk "*"). 
```
(de greeting ()
  (prinl "Hello " *Name "!"))
```
Then we need to pass our command line argument to the global variable ``*Name`` by
```
(setq *Name (opt)) 
```
where ``opt`` is a pre-defined functiont hat retrieves the next command line option. 

The full program so far:

```
#! /usr/bin/picolisp /usr/lib/picolisp/lib.l

(de greeting ()
  (prinl "Hello " *Name "!"))

(setq *Name (opt))
(greeting)

(bye)
```

Now we can pass the name to our script by ``./greetings.l Mia``.

```
$ ./greetings.l Mia
Hello Mia!
```
It works!

But what if we don't pass any argument? Then the name just stays blank:

```
$ ./greetings.l
Hello !
```
It would be nice if the program would tell us about the name option. Let's write a little check to see if the global variable ``*Name`` is empty. If yes, let's print out a warning, otherwise the ``greeting`` function should be called. For this we can use a simple ``ifn`` ("if not") statement:

```
(ifn *Name
  (prinl "Please specify a name!")
  (greeting) )
```

Now let's try again:
```
$ ./greeting-with-args.l 
Please specify a name!

$ ./greeting-with-args.l Mia
Hello Mia!
```


The full script can be downloaded [here](https://gitlab.com/picolisp-blog/single-plage-scripts/-/blob/main/beginners-tutorial/greeting-with-args.l).

-----------------------------

In the last part of the beginner's series, we will talk about the documentation and debugging functions of PicoLisp.

-----------------------------

### Sources
- https://software-lab.de/doc/index.html  
- https://software-lab.de/doc/tut.html  

### Gitlab links:
- https://gitlab.com/picolisp-blog/single-plage-scripts/-/blob/main/beginners-tutorial/greeting-script.l  
- https://gitlab.com/picolisp-blog/single-plage-scripts/-/blob/main/beginners-tutorial/greeting-executable.l  
- https://gitlab.com/picolisp-blog/single-plage-scripts/-/blob/main/beginners-tutorial/greeting-with-args.l