The VikiD Truth

Knowledge base / Computing / Lisp family

picos

computingFreedom | Build | Picolisp | Picolisp Machine | Pil sources | Pil Tutorials | Linux | BASH | C Programmming | Operating Systems | JavaScript | Emacs | vim | Pharo Smalltalk | Computer Security | Penti

Input / Output IO

Here is the truth about I/O in PicoLisp: everything is a stream or a list of characters, and the same functions that print to your terminal can print into a socket, a file or a string. Once you meet in, out and prin, the rest is composition.

What it is, in plain words

PicoLisp I/O is built from a few ideas:

There is no separate “file object” API to memorise – you bind the current stream and use the same readers and writers.

Why it matters

Web handlers, CGI, templating and DB dumps in PicoLisp are all I/O redirection plus printing. Understanding out/in means understanding how Pil apps talk to the world without a maze of classes.

An everyday analogy: a funnel you can aim

Instead of every tool having its own hose, PicoLisp gives you one funnel (prin) and lets you aim it at the sink, the bucket or the neighbour’s tank (out "file", out (pipe …), HTTP response). Same funnel, different target.

flowchart LR
  B[body of code] --> P[prin / print]
  P --> C[current output]
  O["(out target …)"] --> C

A hands-on example

# Write a file
(out "hello.txt"
   (prinl "Hello from Vikix")
   (println (date) (time)) )

# Read it back
(in "hello.txt"
   (until (eof)
      (println (line T)) ) )

# Build a string without printing at all
(pack "x=" 3 " y=" 4)            # -> "x=3 y=4"

# Capture what some code prints: run it in a pipe, read it back
(pipe (prinl "captured!") (line T))   # -> "captured!"

# Read from another program: a list as the "file" runs it
(in '("uname" "-a")
   (prinl (line T)) )

# HTTP server fragment (after loading http/xhtml)
(de go ()
   (html 0 "Hi" NIL NIL
      (<h1> NIL "Hello")
      (<p> NIL "Uptime thoughts go here") ) )

print produces a readable, read-able representation (with quotes); prin prints raw. Choose consciously – especially when emitting HTML (where @lib/xhtml.l helpers already escape).

Common misconceptions

Pretty-printing and debugging

(pretty Expr) and friends help you see structure. During HTTP development, temporarily (out 2 (println …)) to write traces to stderr without corrupting the response body. For large dumps, prefer writing files with out over flooding the console.

Remember that read reads one Pil expression – great for config files written as s-expressions, surprising if you expected line-oriented text (use line for that).

Reading structured data

Because read understands PicoLisp syntax, the easiest config file format is PicoLisp itself:

# settings.l contains:  (theme . "dark") (font . "Iosevka") (size . 14)
(in "settings.l"
   (make
      (while (read)
         (link @) ) ) )
# -> ((theme . "dark") (font . "Iosevka") (size . 14))

For line-oriented text such as /etc/passwd, combine line with split:

(in "/etc/passwd"
   (until (eof)
      (let F (split (line) ":")
         (prinl (car F) " -> " (last F)) ) ) )

(line) without T returns a list of characters, which split cuts on ":"; prinl happily prints those character lists. Use (line T) when you want a string straight away. till reads up to a delimiter, and skip jumps over whitespace and comments, which is all you need for small parsers.

Talking to other programs

A list where a file name would go means “run this program”. That works for both in and out:

(in '("ls" "-1" "/var/service")          # Void's enabled runit services
   (until (eof) (prinl "service: " (line T))) )

(out '("sort" "-r")                       # feed another program
   (mapc prinl '("pear" "apple" "fig")) )

call runs a program and waits, returning T on success, which is perfect for scripts that glue Unix tools together. For JSON, load @lib/json.l and use readJson and printJson, so web APIs fit into the same stream model.

Where the streams go

flowchart LR
  F[file] --> I["(in ...)"]
  P[program] --> I
  S[socket] --> I
  I --> R[read / line / char / till]
  W[prin / print / prinl] --> O["(out ...)"]
  O --> F2[file]
  O --> P2[program]
  O --> S2[socket / HTTP]

Connections

Comments can be embedded in the input stream with the hash # character. Everything up to the end of that line will be ignored by the reader.

: (* 1 2 3)  # This is a comment
-> 6

A comment spanning several lines (a block comment) may be enclosed between

#{ and }#

Examples checked on PicoLisp 26.9, October 2026. Note that the xhtml tag functions take a CSS/attribute argument first, NIL if you have none.

Last edited · 1 revisions

Tags: #Computing