60 PicoLisp Functions You Should Know - 5: Defining Own Functions

Search for a command to run...

No comments yet. Be the first to comment.
This is for users who want to start with PicoLisp from scratch. This series will cover installation, basic concepts, syntax, how to use the REPL, create simple scripts, read the docs and debug.
Welcome back! This is the last post of the 60 most common PicoLisp functions series. Today we will cover lists and strings. We will handle the topic lists quite extensively as it is one of the most important data types in PicoLisp. After that, we wil...
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...

Welcome back to the 60 most common PicoLisp functions series.
Today we will cover 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:
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".
Functions can be created, modified and stored in variables.
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 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"))
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
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
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
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.
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.
Cover: https://unsplash.com/@roman_lazygeek
https://software-lab.de/doc/ref.html#fun
https://software-lab.de/doc/faq.html#lambda