60 PicoLisp Functions You Should Know - 5: Defining Own Functions
Tags: picolisp, lisp, functional, function
Published: 2021-10-25 10:39:57

Welcome back to the **60 most common PicoLisp functions** series.

Today we will cover **functions**. 

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

### What are functions?

Just like pieces of data can be assigned to a specific **variable name**, functions are basically a set of instructions assigned to a specific **function name**. 

So, what is a function in PicoLisp? **A function is a piece of code which happens to be executable.** Since you followed the tutorial up to this point, you might have played with the REPL quite a bit. Maybe you noticed that for each statement you type, you get a **return value** (the part that comes after the ``->``). 

What do we learn from that? Obviously, not only functions are executable, **everything in PicoLisp is executable**. Some consequences of this are:

1. Usually, the return value doesn't need to be defined explicitly. The code inside the function evaluates per definition, and this is equivalent to the "return value".

2. Functions can be created, modified and stored in variables. 

3. Functions can take other functions as arguments, and can give functions as return values.

In fact, **PicoLisp absolutely doesn't make a formal difference between function and data**. This has some interesting implications as we will see later on, especially when we get deeper into functional programming concepts.

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

### Functions without arguments

Functions are defined by ``de``. No tutorial without a "print Hello World" function - here it is:

```
: (de hello ()
   (prinl "Hello World") )

: (hello)
Hello World      
-> "Hello World"     
```
The ``()`` in the first line indicates a function without arguments. The body of the function is in the second line, consisting of a single statement. 

As you can see, the function first *prints* ``Hello World``, then it *evaluates* to ``"Hello World"``. Therefore it is perfectly possible to pass this function as **argument** to another function, for example like this:

```
: (println (hello))
Hello World   
"Hello World" 
-> "Hello World"  
```

As already mentioned, line breaks and indentation are just needed for better readibility, it could as well also be written in one single line:

```
:(de hello() (prinl "Hello world"))
```

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

### Functions with a defined number of arguments

A function with one argument might be defined this way:

```
: (de hello (X)
   (prinl "Hello " X) )
# hello redefined
```

Since the function "hello" already existed from our previous example, PicoLisp informs you that you have just redefined the function. This might be a useful warning in case you forgot that a bound symbol with that name already existed.

Let's test it:

```
: (hello "World")
Hello World
-> "World"

: (hello "Mia")
Hello Mia
-> "Mia"
```

Why does our new function now evaluate only to ``"World"`` instead of ``"Hello World"``?  Inside our ``hello`` function, we are calling the ``prinl`` function with two arguments. ``prinl`` takes the arguments one by one. The last one is ``"World"``, therefore it is the final evaluation of our ``hello`` function and thus it is returned.

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

Functions with two or more arguments are analogous to those with one argument:

```
: (de multiply (X Y) (* X Y))

: (multiply 3 4)
-> 12
```

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

### Functions with an arbitrary number of arguments

If you want to pass an **arbitrary number of arguments** to a function, PicoLisp recognizes the symbol @ as a single atomic parameter. The arguments can then be accessed sequentially with the ``args``, ``next``, ``arg`` and ``rest`` functions. 

Let's write a function ``foo`` that prints out its value and its square value for each argument.

```
: (de foo @
   (while (args)                    # Check if there are some args left
      (let N (next)
         (println N (* N N)) ) ) )
```

``while (args)`` checks if there are some arguments left. ``let N (next)``sets ``N`` to the next argument. Note that ``let N`` defines ``N`` only in a local scope. ``N`` is not accessible outside the function.

Now let's run the example with five arguments: two of them are integers, three of them are expressions that need to be evaluated first. For example, (+ 2 3) is evaluated as 5 before it is passed on to the function.

```
: (foo 2 (+ 2 3) (- 7 1) 1234 (* 9 9))
2 4
5 25
6 36
1234 1522756
81 6561
```

This next example shows the behaviour of ``args`` and ``rest``:

```
: (de foo @
   (while (args)
      (println (next) (args) (rest)) ) )
: (foo 1 2 3)
1 T (2 3)
2 T (3)
3 NIL NIL
```

The ``arg`` function takes an integer as argument and returns the n'th remaining argument: 

```
: (de foo @
   (println (arg 1) (arg 2))
   (println (next))
   (println (arg 1) (arg 2)) )

: (foo 'a 'b 'c)
a b
a
b c
```

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

### Anonymous functions

Some of you might have expected to read now about a ``lambda`` function to create anonymous functions. However in PicoLisp there is no need for that: as we already know, the ``quote`` function (also written as `'`) escapes the evaluation of an expression. Using this, anonymous functions can be created:

```
: ((quote (X) (* X X)) 9)
-> 81

:# equivalent to:
:( '((X)(* X X)) 9)
-> 81
```

### If data and functions are the same, then why do we need the ``de`` keyword then after all?

Actually, it is not needed. 
The ``setq`` keyword for defining variables could also be used to define functions, as this example shows:

```
: (setq f '((X) (* X X)))  

: f
-> ((X) (* X X))

: (f 3)
-> 9
```

Note that this only works because the evaluation of ``'((X) (* X X))`` is escaped due to the quote. Otherwise, the interpreter will try to evaluate ``X`` and complain if it's undefined:

```
: (setq f ((X)(*X X)))
!? (X)
X -- Undefined
```

The interchangeability of data and functions is a key point in PicoLisp, but might be somehow difficult to grasp in the beginning. We will come back to that point later.

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

### Calling external functions

External system commands can be called using ``call``. If the command was executed successfully, ``T`` is returned. The (system dependent) exit status code of the child process is stored in the global variable @@. 

```
: (call 'whoami)
mia

: (when (call 'test "-r" "file.l")  # Test if file exists and is readable
    (load "file.l")  # Load it
    (call 'rm "file.l") )  # Remove it
```

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

Congratulations, we have now almost finished our "60 most common PicoLisp functions" series! The last post will cover **Lists and String functions**.

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

### Source

- Cover: https://unsplash.com/@roman_lazygeek  
- https://software-lab.de/doc/ref.html#fun  
- https://software-lab.de/doc/faq.html#lambda