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
  • REPL
  • 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"

Good!

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 pil +).

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! In picolisp, format is used only to format numbers:

: (format 1234567890 2 "." ",")
-> "12,345,678.90"

What I wanted is maybe prin:

? (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 what defun is but that's ok, I can buffer that.

Loading into the REPL things defined in a separate .lisp file?

: (load "hello.lisp")
-> hello
: (hello)
"hello world"-> "hello world"

Good.

Data structures

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)
  • lists

So, lists?

: (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

Symbols

: (set 'foo '1234)
-> 1234
: foo
-> 1234

That says "set the value of the symbol foo to 1234. It has a quote in 'foo, because we don't want to evaluate foo. If we did, then we would assign 1234 to the result of evaluating foo, which is surely not what you want?

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 quote:

: (setq foo '1234)
-> 1234
: foo
-> 1234

In these specific examples, the quote is not needed before '1234, since 1234 evaluates to 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 foo symbol:

: (+ 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 car, cdr, and cons.

Yes, the first thing those who don't know LISP know about LISP is "OMG parenthesis".

So, what are car, cdr, and cons? Mostly the problem with them is their name. If 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

So, car is a function that takes a list and returns the first element in it.

And if car is first, then cdr is rest:

: (cdr (1 2 3))
-> (2 3)

And cons means glue:

: (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[3] in python?

? (nth 2 (1 2 3 4))
-> 2

What nth does is repeatedly call car so it turns out lists are 1-indexed :-) There are a number of other functions to access lists:

  • caar
  • cdar
  • caaar
  • c-a-few-a's-and-d's-r

What they do is call car and cdr repeatedly. So (cadar 'foo) means (car (cdr (car 'foo)):

? (setq foo '(1 2 3 4 5))
-> (1 2 3 4 5)
? (cadar 'foo)
-> 2

Why is it 2?

? (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 foo modified.

There are destructive list operations in other LISP variants, such as setcar and setcdr, but I have not found them in picolisp.

Logic

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 function 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.

Loops

There is an easy do:

: (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!

Comments

Comments powered by Disqus