Commit 60d8dd4d authored by Marco Antoniotti's avatar Marco Antoniotti 💬
Browse files

Added 'introduction.html' documentation file.

parent b1bd0660
<!-- introduction -->
<html>
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<head>
<title>With Common Lisp Contexts</title>
<meta http-equiv="Content-Type"
content="text/html"
charset="UTF-8" />
<link rel="stylesheet" href="helambdap.css" />
</head>
<body>
<h1><a name="With Common Lisp Contexts">With Common Lisp Contexts</a></h1>
<p>
<strong>Common Lisp</strong>
programmers may write
many <code>with-<i>something</i></code> overt their careers; the
language specification itself is ripe with such constructs:
withness <code>with-open-file</code>. Many other libraries also
introduce a slew of with- macros dealing with this or that case.
</p>
<p>
So, if this is the case, what prevents <strong>Common
Lisp</strong> programmers from coming up with a
generalized <strong><code>with</code></strong>
macro?
</p>
<p>
It appears that the question has been answered, rather
satisfactorily, in <a href="https://www.python.org"
target="_blank">Python</a> and <a href="https://julialang.org"
target="_blank">Julia</a> (at least). Python offers
the <a href="https://docs.python.org/3/reference/compound_stmts.html#the-with-statement"
target="_blank">with</a> statement, alongside a library of
<i>"<a href="https://docs.python.org/3/reference/datamodel.html#context-managers"
target="_blank">contexts</a>"</i>
(Python introduced the <strong>with</strong> statement in 2005
with <a href="https://www.python.org/dev/peps/pep-0343/"
target="_blank">PEP 343</a>) and Julia offers
its <a href="https://docs.julialang.org/en/v1/manual/functions/#Do-Block-Syntax-for-Function-Arguments"
target="_blank"><strong>do</strong></a> blocks.
</p>
<p>
The present library, WITH-CONTEXTS, is a <strong>Common
Lisp</strong> answer to the question. The library is patterned
after the ideas embodied in the Python solution, but with several
(common) "lispy" twists.
</p>
<p>Here is the standard - underwhelming - example:
<pre>
(<b>with</b> <i>f</i> <b>=</b> (open "foo.bar") <b>do</b>
(do-something-with <i>f</i>))
</pre>
That's it as far as syntax is concerned
(the '<code><i>var</i> <b>=</b></code>' being optional, obviously
not in this example; the syntax was chosen to
be <b><tt>loop</tt></b>-like, instead of using
Python's <b><tt>as</tt></b> keyword). Things become more
interesting when you look under the hood.
</p>
<p>
Traditional Common Lisp with- macros expand in variations of
unwind-protect or handle-case (and friends). The example above, if
written with with-open-file would probably expand into something
like the following:
<pre>
(let ((f nil))
(unwind-protect
(progn
(setq f (open "foo.bar"))
(do-something-with f))
(when f (close f))))
</pre>
</p>
<p>
Python generalizes this scheme by introducing
a <i>enter</i>/<i>exit</i> protocol that is invoked by
the <b><tt>with</tt></b> statement. Please refer to the Python
documentation
on <i><a href="https://docs.python.org/3/reference/datamodel.html#context-managers"
target="_blank">contexts</a></i> and
their <b><tt>__enter__</tt></b> and <b><tt>__exit__</tt></b>
methods.
</p>
<h2>The "WITH" Macro in Common Lisp: Contexts and Protocol</h2>
<p>
In order to introduce a with macro in Common Lisp that mimicked
what Python programmers expect and what Common Lisp programmers
are used to some twists are necessary. To achieve this goal, a
protocol of <i>three</i>
generic functions is provided alongside a
library of <i>contexts</i>.
</p>
<h3>The ENTER/HANDLE/EXIT Context Protocol</h3>
<p>
The WITH-CONTEXTS library provides three generic functions that
are called at different times within the code resulting from the
expansion of the onvocation of the <b><tt>with</tt></b>
macro.
<ul>
<li><b>enter</b>: this generic function is invoked when
the <b><tt>with</tt></b> macro "enters" the context; its main
argument is the result of the expression that is the
argument of the <b><tt>with</tt></b> macro.
</li>
<li><b>handle</b>: this generic function is called to take care
of exceptional situations that may arise during the call
to <b><tt>enter</tt></b> or during the execution of the body of
the <b><tt>with</tt></b> macro.
</li>
<li><b>exit</b>: this generic function is called to "clean up"
before exiting the context entered by means of
the <b><code>with</code></b> macro.
</li>
</ul>
</p>
<p>
Given the protocol (from now on referred to as the "<b>EHE-C
protocol</b>"), the (undewhelming) "open file" example expands in
the following:
<pre>
(let ((<i>f</i> nil))
(unwind-protect
(progn
(setq <i>f</i> (<span style="color: blue"><b>enter</b></span> (open "contexts.lisp")))
(handler-case (open-stream-p <i>f</i>)
(error (#:ctcx-err-e-41883)
(<span style="color: blue"><b>handle</b></span> <i>f</i> #:ctcx-err-e-41883))))
(<span style="color: blue"><b>exit</b></span> <i>f</i>)))
</pre>
</p>
<p>
Apart from the <code>gensym</code>med variable the expansion is
pretty straightforward. The function<b> <tt>enter</tt></b> is
called on the newly opened stream (and is essentially an identity
function) and sets the variable. If some error happens while the
body of the macro is executing then control is passed to
the <b><tt>handle</tt></b> function (which, in its most basic form
just re-signals the condition). Finally, the unwind-protect has a
chance to clean up by calling <b><tt>exit</tt></b> (which, when
passed an open stream, just closes it).
</p>
<p>
One unexpected behavior for Common Lisp programmers is that the
varable (f in the case above) escapes the with constructs. This is
in line with what Python does, and it may have its uses. The file
opening example thus has the following behavior:
<pre>
CL-prompt > <b>(<span style="color: blue">with</span> (open "contexts.lisp") as f do
(open-stream-p f))</b>
<i>T</i>
CL-prompt > <b>(open-stream-p f)</b>
<i>NIL</i>
</pre>
</p>
<p>
To ensure that this behavior is reflected in the implementation,
the actual macroexpansion of the <b><tt>with</tt></b> call becomes
the following.
</p>
<p>
<pre>
(let ((#:ctxt-esc-val-41882 nil))
(multiple-value-prog1
(let ((<i>f</i> nil))
(unwind-protect
(progn
(setq <i>f</i> (<span style="color: blue"><b>enter</b></span> (open "contexts.lisp")))
(handler-case
(open-stream-p <i>f</i>)
(error (#:ctcx-err-e-41883)
(<span style="color: blue"><b>handle</b></span> <i>f</i> #:ctcx-err-e-41883))))
(multiple-value-prog1
(<span style="color: blue"><b>exit</b></span> <i>f</i>)
(setf #:ctxt-esc-val-41882 <i>f</i>))))
(setf <i>f</i> #:ctxt-esc-val-41882)))
</pre>
</p>
<p>
This "feature" will help in - possibly - porting some Python code
to <b>Common Lisp</b>.
</p>
<h3>"Contexts"</h3>
<p>
Python attaches to the <b><tt>with</tt></b>
statement the notion of <em>contexts</em>. In
<strong>Common Lisp</strong>, far as the with macro is concerned,
anything that is passed as the expression to it, must respect the
<b><tt>enter</tt></b>/<b><tt>handle</tt></b>/<b><tt>exit</tt></b>.
protocol. The three generic
functions <b><tt>enter</tt></b>, <b><tt>handle</tt></b>, <b><tt>exit</tt></b>,
have simple defaults that essentially let everything "pass
through", but specialized context classes have been defined that
parallel the
Python <a href="https://docs.python.org/3/reference/datamodel.html#context-managers"
target="_blank">context library</a> classes.
</p>
<p>
First of all, the current library defines the EHE-C protocol for
<i>streams</i>. This is the strightforward way to obtain the
desired behavior for opening and closing files as
with <b><tt>with-open-file</tt></b>.
</p>
<p>
Next, the library defines the following "contexts" (as Python does).
<ul>
<li><b><tt>null-context</tt></b>:<br />
this is a full "pass
through" context, just encapsulating the expression passed to
it.
</li>
<li><b><tt>managed-resource-context</tt></b>:<br />
this is a
first cut implementation of a "managed resource", which
implements also
an <b><tt>acquire</tt></b>/<b><tt>release</tt></b> protocol;
of course, this would become more useful in presence of
mutltiprocessing (see notes
in <a href="#Limitations">Limitations</a>).
</li>
<li><b><tt>redirect-context</tt></b>:<br />
this is a context that redirects output to a different
stream.
</li>
<li><b><tt>suppress-context</tt></b>:<br />
this is a context that selectively handles some conditions,
while ignoring other ones.
</li>
<li><b><tt>exit-stack-context</tt></b>:<br /> this is a context
that essentially allows a programmer to manipulate the "state
of the computation" within it body and combine other
"contexts"; to achieve its design goal, it leverages the
functions of a protocol comprising the <b><tt>enter-context</tt></b>,
<b><tt>push-context</tt></b>,
<b><tt>callback</tt></b>,
<b><tt>pop-all</tt></b> and <b><tt>unwind</tt></b> (this is
equivalent to the Python <tt>close()</tt> context method).
</li>
</ul>
</p>
<p>
This should be a good enough base to start working with contexts
in Common Lisp. It is unclear whether the Python decorator
interface would provide some extra functionality in this Common
Lisp implementation of contexts and the <b><tt>with</tt></b>
macro.
</p>
<h2><a name="Limitations">Limitations</a></h2>
<p>
The current implementation has a semantics that is obviously not
the same as the corresponding Python one, but it is hoped that it
still provided useful functionality. There are some obvious
limitations that should be taken into account.
</p>
<p>
The current implementation of the library does not take into
consideration <em>threading issues</em>. It could, by providing
a <b><tt>locking-context</tt></b> based on a portable
multiprocessing API
(e.g., <a href="https://common-lisp.net/project/bordeaux-threads/"
target="_blank"><b><tt>bordeaux-threads</tt></b></a>).
</p>
<p>
The Python implementation of contexts relies heavily on
the <b><tt>yield</tt></b> statement. Again, the current
implementation does not provide similar functionality, altough it
could possibly be implemented using a <em>delimited continuation
library</em>
(e.g., <a href="https://common-lisp.net/project/cl-cont/"
target="_blank"><b><tt>cl-cont</tt></b></a>).
</p>
</body>
</html>
<!-- end of file : introduction -->
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment