The VikiD Truth

Knowledge base / Computing / Lisp family

picos

The Vikid PicoLisp Tutorial

These are the REPL habits I picked up when I learned PicoLisp. I re-ran every snippet on PicoLisp 26.9 (pil21) in October 2026 and updated what had changed. The core language has barely moved since I first wrote this. What did change is the tooling around it: the editor, namespaces, and how you install it.

Start PicoLisp

Build pil21 (see PicoLisp Installation) and start it in debug mode:

$ pil +          # after a global install
$ ./pil +        # or from inside the unpacked pil21 directory

The + matters: it loads the debug tools (vi, doc, trace, debug, what, who, can, dep…). Without it you get a plain runtime. Leave with (bye) or Ctrl-D.

Symbols, values and properties

Set some values:

(setq A '(This is the value))  # Set the value of 'A'
(put 'A 'key1 'val1)           # Store property 'key1'
(put 'A 'key2 'val2)           # and 'key2'

Inspect them with show:

(show 'A)                      # Now 'show' the symbol 'A'
A (This is the value)
   key2 val2
   key1 val1
(put 'B 'a 'A)        # Put 'A' under the 'a'-property of 'B'
(setq Lst '(A B C))   # Create a list with 'B' as second element
(show Lst 2 'a)       # Show the property 'a of the 2nd element of 'Lst'

Editing: vi and v

(vi '(A B))
(v A B)

In pil21, vi opens Vip, PicoLisp’s own vi-style editor (written in PicoLisp, in @lib/vip.l). It edits the source of a function, or the value and properties of a symbol. When you write and quit, the change is read back into the running system. v does the same for symbols without quoting. Vip also has some newer extras: :ai and :web (an LLM client, via @lib/ai.l), and :cal for a calendar.

Looking around the system

The pretty-print function pp takes a symbol that has a function defined (or two symbols that specify message and class for a method definition):

(pp 'pretty)

more displays the elements of a list one by one. Optionally it takes a function as a second argument and applies it to each element:

(more (1 2 3 4 5 6))
(more '(A B) show)

what returns all internal symbols that match a pattern (with @ wildcards):

(what "prin@")
-> (prin prinl print print> println printsp)

who returns “who contains that”: a list of symbols that contain a given argument somewhere in their value or property list:

(who 'print)

can tells you which classes accept a given message, and dep shows the dependencies in a class hierarchy:

(can 'del>)
-> ((del> . +Swap) (del> . +List) (del> . +Remote) (del> . +Entity) (del> . +relation))
(dep '+relation)

New in pil21: symbols live in namespaces. (symbols) shows the current search order, and pico~foo names a symbol in a specific namespace. Some libraries (for example @lib/simul.l, @lib/ai.l) define their own namespace, so call (symbols '(simul pico)) or use the simul~ prefix.

Defining functions

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

A function with one argument:

(de hello (X)
   (prinl "Hello " X) )
(hello "world")

To prevent argument evaluation and take a variable number of arguments, give the definition a single atomic parameter instead. PicoLisp will then bind all the (unevaluated) arguments as a list to that parameter:

(de foo X
   (list (car X) (cadr X)) )

Mixing evaluated arguments and a variable number of unevaluated arguments:

(de foo (X Y . Z)     # Evaluate only the first two args
   (list X Y Z) )

A variable number of evaluated arguments:

(de foo @
   (list (next) (next)) )

(de foo (X Y . @)
   (list X Y (next) (next)) )

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

(de foo @
   (pass println 9 8 7)       # First print all arguments preceded by 9, 8, 7
   (pass + 9 8 7) )           # Then add all these values

There’s no distinction between code and data in PicoLisp, so quote will do what you want (see quote):

((quote (X) (* X X)) 9)    # -> 81
(setq f '((X) (* X X)))    # This is equivalent to (de f (X) (* X X))

Debugging: tracing and single-stepping

You can trace and debug from the command line:

$ pil app/file1.l -"trace 'foo" -main -"debug 'bar" app/file2.l +

Or from the REPL:

(de fact (N)
   (if (=0 N)
      1
      (* N (fact (dec N))) ) )
(trace 'fact)
(trace '=0)
(trace '*)
(fact 3)
(untrace 'fact)
(mapc untrace '(fact =0 *))   # mapc applies a function to each element

Debugging with debug:

(pp 'stamp)
(debug 'stamp)
(stamp)
(unbug 'stamp)

When an error happens you land in a break REPL (prompt !). There, (bt) prints a backtrace, ^ holds the offending expression, and ENTER returns to the top level. (lint 'foo) and (lintAll) catch unused or undefined variables before you run anything.

Functional I/O

(read)
(in "@lib.l" (read))
(in "@lib.l" (skip "#") (char))
(in "@lib.l" (while (from "nond") (println (read))))
(in "@lib.l" (from "(de ") (till " " T))
(in "f.l" (till NIL T))
(in "@doc/tut.html" (line T))
(in "@doc/tut.html" (line))

@ at the start of a path means “the PicoLisp home directory”, wherever it is installed. There is more on this in Input/Output.

NIL

Besides the standard form, NIL is also recognised as () or "":

: NIL
-> NIL
: ()
-> NIL
: ""
-> NIL

Output will always appear as NIL. NIL is the empty list, false, and the end of every list all at once (see Find Boxes in Lists).

Where next

Last edited · 18 revisions

Tags: #Computing