(rontolisp) docs

The uiop Package

uiop is ASDF's portability layer — the spelling implementation-independent libraries already use for the operations Common Lisp never standardized: reading an environment variable, probing a file, walking a directory, splitting a string. It is not part of Common Lisp; reference its symbols with a qualifier (uiop:getenv), never unqualified.

The coverage target is uiop 3.3.7, the release the built-in ql:quickload client fetches. That release exports 429 symbols, and rontolisp implements a subset of them; the rest resolve and signal, so a library that merely names one in an (:import-from #:uiop) clause still reads, compiles and runs.

Sub-packages

Upstream's uiop is uiop/driver, a re-export of 15 sub-packages, and a library may name either spelling — lack-middleware-backtrace writes (:import-from :uiop/image :print-condition-backtrace). rontolisp registers all 15, with each sub-package owning the members it defines and uiop importing them, so both spellings name the same symbol rather than two functions with one member name:

Sub-packageWhat lives thereImplemented
uiop/packagesymbol and package surgery (find-symbol*, intern*, define-package)4 / 31
uiop/package-local-nicknamesthe package-local nickname API1 / 3
uiop/package*the three condition/type names uiop/package defines but does not export0 / 3
uiop/utilitythe portable helpers (strcat, split-string, if-let, not-implemented-error)68 / 68
uiop/versionversion comparison and the deprecation conditions1 / 15
uiop/oshost identity, the environment, the working directory22 / 22
uiop/pathnamethe pathname algebra (subpathname, parse-unix-namestring, enough-pathname)50 / 50
uiop/filesystemprobe, walk and mutate the file system8 / 32
uiop/streamfile contents, temporary files, encodings, the standard streams3 / 66
uiop/imageexit, fatal conditions, the dump hooks (and the command line, which is not implemented)25 / 30
uiop/launch-programasynchronous subprocesses0 / 19
uiop/run-programsynchronous subprocesses0 / 7
uiop/lisp-buildcompile-file* and the deferred warnings1 / 44
uiop/configurationXDG paths and the configuration search0 / 38
uiop/backward-driverthe deprecated aliases0 / 7

The full export list is checked in as src/main/resources/am/ik/rontolisp/uiop-exports.txt (one row per export: sub-package, symbol, and the definition form upstream gives it). It is the target the counts above are measured against, so both move together.

What is implemented

Four sub-packages have their own page. Three are complete: uiop/utility — the 68 portable helpers everything else in uiop is written in (uiop/utility) — uiop/pathname, the 50-member pathname algebra (uiop/pathname), and uiop/os, the 22 host-identity, environment and working-directory members (uiop/os, which is where uiop:getenv lives). The fourth is uiop/image, where uiop:quit ends the process with a status code on all four backends, and where the fatal-condition, backtrace and image-hook families live. The rest:

FunctionExampleResult
uiop:file-exists-p(uiop:file-exists-p "f.txt")the pathname when the file exists, nil otherwise — the same contract as probe-file, which it lowers onto on every backend
uiop:directory-exists-p(uiop:directory-exists-p "src/")the pathname (with a trailing /) when the DIRECTORY exists, nil otherwise — the directory twin of file-exists-p, and what tells an empty directory from a missing one
uiop:directory-files(uiop:directory-files "db/" "*.up.sql")the non-directory entries of a directory — (directory "db/*.*") with the subdirectories dropped. UIOP's optional second argument, the namestring of a name-and-type wildcard, filters them exactly as directory matches; omitting it lists everything, and a pattern carrying a directory component is an error
uiop:subdirectories(uiop:subdirectories "src/")the subdirectories of a directory, each with its trailing /
uiop:collect-sub*directories(uiop:collect-sub*directories "src/" (constantly t) (constantly t) #'print)walk a directory tree: collectp decides what reaches collector, recursep what is descended into. Every directory handed over is in directory form, root included
uiop:read-file-string(uiop:read-file-string "db/up.sql")the whole file as one string. Runs on every backend that can open a file for input. Lite: real UIOP's &rest keys are accepted and ignored (:external-format has no rontolisp surface — every backend reads UTF-8)
uiop:compile-file-type(uiop:compile-file-type)nil — the pathname type a compiled file carries. There is no compile-file here, so there is no such type, and a caller asking "is this path a fasl?" gets no for a source path
uiop:default-temporary-directory(uiop:default-temporary-directory)$TMPDIR in directory form, or #P"/tmp/" when the environment is empty (both WASM backends without --env)
uiop:delete-file-if-exists(uiop:delete-file-if-exists "scratch.txt")delete a file, answering nil instead of signalling when it is not there — the whole reason UIOP exports it
uiop:get-pathname-defaults(uiop:get-pathname-defaults)the defaults relative names resolve against — *default-pathname-defaults* (initially #P"", the pathname designating the host working directory) unless an absolute defaults argument is given
uiop:native-namestring(uiop:native-namestring #P"/tmp/x")"/tmp/x" — the host-OS spelling of a pathname, which here IS the namestring, so this is namestring
uiop:add-package-local-nickname(uiop:add-package-local-nickname '#:j '#:com.example.pkg)register a package shorthand (lite: global, no per-package scoping). A literal top-level call is a compile-time directive, so it works on every backend
uiop:symbol-call(uiop:symbol-call :cl :+ 1 2)look the name up in the package at run time and apply it — UIOP's late-binding call into a system the caller does not depend on

Three members outside the complete sub-packages are macros, expanded by the compiler rather than called: uiop:with-temporary-file, uiop:with-deprecation and uiop:define-package (a literal top-level call is consumed like defpackage). uiop/pathname's two macros — uiop:with-pathname-defaults and uiop:with-enough-pathname — are on its page. uiop/utility's own macros — uiop:if-let, uiop:nest, uiop:while-collecting, uiop:with-upgradability and the rest — are on its page.

What is not

Every other export resolves and signals uiop:not-implemented-error, naming the operation. That is the whole point of registering the inventory: a program that reaches an unfilled corner of uiop gets one clear answer instead of an undefined function from the middle of a library, and a handler can catch it:

$ rontolisp -e '(uiop:run-program "ls")'
Unhandled condition: Not (currently) implemented on rontolisp: UIOP/RUN-PROGRAM:RUN-PROGRAM

The behaviour is identical on all four backends — the interpreter, the JVM and both WASM outputs signal the same condition with the same report.

rontolisp extras

Two names live in uiop that upstream does not export there:

  • uiop:namestring — upstream only inherits Common Lisp's; here it is exported and is the very namestring function, so both spellings name one function.
  • uiop:when-let and uiop:when-let* — alexandria's names, kept because programs already spell them. Real UIOP exports if-let only.