The WITH-CONTEXT System. A system providing a WITH macro and 'context'ualized objects handled by a ENTER/HANDLE/EXIT protocol in the spirit of Python's WITH macro. Only better, or, at a minimum different, of course.
Copyright (c) 2020-2023 Marco Antoniotti See file COPYING for licensing information
This library contains an implementation of a
WITH macro with
"contexts" inspired by Python, which, in turn was obviously inspired
by Common Lisp macros (and other earlier languages, like Pascal).
The Python library is described in the documentation of the
contextlib documentation. The current library is an implementation
that overlaps with the Python one, as a few things are available in
Common Lisp that are not available in Python and two things are
present in Python, that are not available in Common Lisp: the
statement and built-in threading for asynchronous computations. Note
yield statement could be built in Common Lisp using a
delimited continuation library like
cl-cont. The asynchronous
extensions could instead be directly built on top of the current
Most of the Python examples described in the ... context of
are directly translatable into Common Lisp using the present library.
The main difference is that, in order to leverage the Common Lisp
Condition subsystem the "protocol" that "contexts" must implement is
comprised of three generic functions:
HANDLE <context> <condition>
The WITH macro is practically expanded as follows.
(with [VAR =] CONTEXT-ITEM do CODE)
(let ((VAR NIL)) (unwind-protect (progn (setf VAR (ENTER CONTEXT-ITEM)) (handler-case CODE (error (e) (HANDLE VAR e)) )) (EXIT VAR)))
With this setup,
WITH-OPEN-FILE can be immediately rewritten as
(with f = (open "some.txt") do (loop for line = (read-line f) while line do (do-stuff-to line)))
provided that the proper
EXIT protocol is in place.
That is, something like the following.
(defmethod enter ((s file-stream) &key) (if (open-stream-p s) s (error "Stream ~S is not open." context-item))) (defmethod handle ((s file-stream) (e error) &key) (call-next-method)) (defmethod exit ((s file-stream) &key) (when (open-stream-p s) (close s)))
Note that in Python,
HANDLE does not exist and
EXIT is called
More Elaborated Contexts
contextlib Python library contains more elaborated "contexts" that
can be used to perform a number of sophisticated operations in
conjunction with the
ExitStack as (the following is a direct quote from
contextlib documentation) a context manager that is designed
to make it easy to programmatically combine other context managers and
cleanup functions, especially those that are optional or otherwise
driven by input data.
For example, a set of files may easily be handled in a single with statement as follows:
(with stack = (exit-stack) do (let* ((files (mapcar (lambda (fname) (enter-context stack (open fname))) *filenames*))) ;; Hold on to the new exit stack (not the method pointe as in the ;; Python example), but don't call its UNWIND method (setf *close-files* (pop-all stack)) ;; If opening any file fails, all previously opened files will be ;; closed automatically. If all files are opened successfully, ;; they will remain open even after the with statement ends. ;; ;; (unwind *close-files*) ;; ;; can then be invoked explicitly to close them all. ;; ... )
Each instance maintains a stack of registered callbacks that are
called in reverse order when the instance is closed (either explicitly
or implicitly at the end of a
Please refer to the full documentation of the
for more details.
A NOTE ON FORKING
Of course you are free to fork the project subject to the current licensing scheme. However, before you do so, I ask you to consider plain old "cooperation" by asking me to become a developer. It helps keeping the entropy level at an acceptable level.
Marco Antoniotti 2023-01-21