Playing With Picolisp (Part 1)
I want to learn new languages. But new as in "new to me", not new as in "created last week". So I decided to play with the grandaddy of all cool languages, LISP. Created in 1958, it's even older than I, which is good because it's experienced.
One "problem" with LISP is that there are a million LISPs. You can use Scheme or Common Lisp, or Emacs' Lisp, or a bazillion others. I wanted something simple so it was supposed to be Scheme... but a few days ago I ran into something called Picolisp and it sounded so cool.
- Interfaces with native libraries
- Purely interpreted, but fast
- Persistent distributed DB builtin (WAT?!)
So, why not.
Well, one reason why not is that there is, as far as I can see, no tutorial anywhere for picolisp the language. There is a "tutorial" but it assumes you know picolisp!
This is not a Lisp tutorial, as it assumes some basic knowledge of programming, Lisp, and even PicoLisp.
-- The picolisp "tutorial"
It does however have a good reference for all the functions available in picolisp by default, which is very handy, and I often groked what something did by using it's examples.
However, picolispers, it's difficult to start with your language if you don't explain your language. So, because I am stubborn, I decided to try it anyway. However, without a decent tutorial, expect this to be less of a "let's implement something using picolisp" and more of a "adventures of a pythonista being frustrated by another language".
But hey, it may work as a tutorial up to a point! Let's go learn enough LISP to be dangerous.
Let's try the REPL:
$ pil : 10 -> 10 : "hello world" -> "hello world"
One bad thing: the REPL has no history and line edition is basic. No problem!
Just use good old rlwrap and you are good to go (Note: it turns out it has editing and history, but you need to start it as
So, having already exhausted my knowledge of LISP by typing values into the interpreter, I decided to just learn some other variant of LISP and see what happened. I found a well regarded book called Practical Common Lisp ... main idea is to learn generic syntax, fill in the blanks, and that should do it. I have been programming for 35 years, surely I can do things the wrong way and get away with it, right?
So... it did not go so well. This is like, the second example in the book:
CL-USER> (format t "hello, world") hello, world NIL
And here is what happens on picolisp:
: (format t "Hello, world") !? (format t "Hello, world") "Hello, world" -- Small number expected
Hmmm... ok, let's try a small number then?
? (format t 2) -> "2671.89"
That is ... unexpected. So, I am clearly missing something here. To the google!
format is used only to format numbers:
: (format 1234567890 2 "." ",") -> "12,345,678.90"
What I wanted is maybe
? (prin "hello world") hello world-> "hello world"
There you can see the side effect (the first
hello world) and then the way picolisp shows the result of the function:
-> "hello world"
Ok, let's ignore the weirdness and go on.
Creating a function
? (de hello () (print "hello world")) -> hello ? (hello) "hello world"-> "hello world"
Hey, nice. So,
de means what comes next is function name and a list of args,
then the function "body". Sure, as expected, the syntax is a bit alien, but I
can work with this.
Of course the CL book says
de should be
defun and picolisp has no idea
defun is but that's ok, I can buffer that.
Loading into the REPL things defined in a separate
: (load "hello.lisp") -> hello : (hello) "hello world"-> "hello world"
It turns out picolisp has exactly 3:
- numbers (not even floating point numbers, just ints)
- strings (in fact it doesn't have strings, although it looks like it does. It's complicated)
: (list 1 2 3) -> (1 2 3) : (list 1 "foo" 3) -> (1 "foo" 3)
In fact, picolisp treats simple lists as lists as long as the 1st element is a number:
: (1 2 3) -> (1 2 3) : ("foo" 2 3) !? ("foo" 2 3) "foo" -- Undefined
: (set 'foo '1234) -> 1234 : foo -> 1234
That says "set the value of the symbol
1234. It has a quote in
'foo, because we don't want to evaluate
foo. If we did, then we would
1234 to the result of evaluating
foo, which is surely not what you
So, if you want something to be what you typed, use the quote, just in case.
Since you 99.999% of the time don't want to evaluate the name of the variable
you are setting, you can use
setq (set quoted) which lets you avoid that
: (setq foo '1234) -> 1234 : foo -> 1234
In these specific examples, the quote is not needed before
1234 but I guess I better make it a habit.
You can assign more than once at a time:
: (setq foo '1234 bar '5678) -> 5678 : (prin foo bar) 12345678-> 5678
So, I can then use the
: (+ 3 foo) -> 1237
Yes, Lisp evaluates lists, so it makes sense for it to use prefix notation. Deal with it like a developer.
What can we do with lists?
That could very well be the name of all LISP books, since 99% of programming
lisp is doing things to lists, with lists and about lists. But let's focus.
Here we will find the second thing people who don't know LISP (such as myself)
know about LISP: that there are weird things called
Yes, the first thing those who don't know LISP know about LISP is "OMG parenthesis".
So, what are
cons? Mostly the problem with them is their
car was called
first everyone would know what it does. But hey,
it was the 50s and people did not give a damn about clarity. I blame alcohol.
: (car (1 2 3)) -> 1
car is a function that takes a list and returns the first element in it.
: (cdr (1 2 3)) -> (2 3)
: (cons 1 '(2 3)) -> (1 2 3)
In other variants of LISP, this doesn't work, but in picolisp it does:
: (cons 1) -> (1)
You can use lists as arguments of functions (of course), so you can do the expected things:
? (length '(a b c)) # How long is that list? -> 3 ? (length "foo") # How long is "foo"? -> 3 : (index 1 (3 2 1)) # Where in the list is the 1? -> 3
And how do you access the "nth" element of a list, like
l in python?
? (nth 2 (1 2 3 4)) -> 2
nth does is repeatedly call
car so it turns out lists are 1-indexed :-)
There are a number of other functions to access lists:
What they do is call
cdr repeatedly. So
(cadar 'foo) means
(cdr (car 'foo)):
? (setq foo '(1 2 3 4 5)) -> (1 2 3 4 5) ? (cadar 'foo) -> 2
Why is it
? (setq foo '(1 2 3 4 5)) -> (1 2 3 4 5) ? (car (cdr (car 'foo))) -> 2
And how do you alter a list?
? (setq foo '(1 2 3 4 5)) -> (1 2 3 4 5) : (insert 2 foo "x") # Insert "x" in position 2 in list foo -> (1 "x" 2 3 4 5) : (remove 2 foo) # Removes element in position 2 in list foo -> (1 3 4 5) : (place 3 foo "x") # Place "x" as element 3 in foo -> (1 2 "x" 4 5) : (setq foo '(1 2 3 4 5 1 2 3 4)) -> (1 2 3 4 5 1 2 3 4) : (delete 3 foo) -> (1 2 4 5 1 2 3 4) # Delete the 3s from foo : (setq foo '(1 2 3 4 5 4 3 2 1)) -> (1 2 3 4 5 4 3 2 1) : (member 5 foo) # The tail that starts when it finds a 5 -> (5 4 3 2 1)
Mind you that is not actually altering the list, it's just returning another
list which is
There are destructive list operations in other LISP variants, such as
setcdr, but I have not found them in picolisp.
How can I make my program do something in one situation and something else in another?
Now LISP is starting to be predictable, which is good. I expect there is a
if that takes 3 arguments, one is evaluated, and depending on its
value, if it's "true" the second will evaluate, or else, the third will. And
voilá, that is so:
: (setq x 4) -> 4 : (if (> x 0) (print "Greater") (print "Smaller")) "Greater"-> "Greater" : (setq x -3) -> -3 : (if (> x 0) (print "Greater") (print "Smaller")) "Smaller"-> "Smaller"
There are others, but
if is enough for now.
There is an easy
: (do 4 (printsp 'OK)) # printsp prints a value and a space. OK OK OK OK-> OK
And a more complicated
do, which I really did not understand:
: (do 4 (printsp 'OK) (T (= 3 3) (printsp 'done))) # WAT? OK done -> done
More interesting for python people is
for which iterates over a list:
: (for X (1 2 3 4 5 4 3 2 1) (printsp X)) 1 2 3 4 5 4 3 2 1 -> 1
I think this is about enough for a first taste of LISP and microlisp. Hopefully I can continue learning it next saturday!
To get the nth element of a list we actually use 'get' and not nth. try (help 'nth T) & (help 'get T) in debug mode for info.
(get (6 5 3 2) 2) # returns the 2nd list element 5
(nth 2 (5 6 7 8 2)) # returns 2 itself
(nth (range 1 9) 5) #return list starting from 5th element
(nth '(a b c d e) 2) # return list from 2nd element