tag:blogger.com,1999:blog-57223106422663560032024-03-13T05:57:44.126+01:00The Axis of EvalManuel Simoni's blog about programming (languages).Unknownnoreply@blogger.comBlogger298125tag:blogger.com,1999:blog-5722310642266356003.post-36402599330444640382024-01-07T20:13:00.015+01:002024-01-21T12:43:09.217+01:00Common Lisp's BLOCK / RETURN-FROM and UNWIND-PROTECT<p>I was just <a href="https://twitter.com/TitzerBL/status/1743775279485452497">chatting with Ben Titzer on Twitter</a> about control flow in his <a href="https://github.com/titzer/virgil">Virgil language</a> (which is cool and you should definitely check out), when I felt the need to <b>once more promote how Common Lisp does non-local control flow</b> (stuff like returning early from a function or breaking from a loop), because I think it's a very nice solution.</p><p>So in Common Lisp we have BLOCK / RETURN-FROM (which work as a pair) and UNWIND-PROTECT.</p><h3>BLOCK / RETURN-FROM</h3><p><a href="http://clhs.lisp.se/Body/s_block.htm">BLOCK</a> and <a href="http://clhs.lisp.se/Body/s_ret_fr.htm">RETURN-FROM</a> effectively offer the same functionality as C's <b>setjmp/longjmp</b> -- non-local exits -- but nicely wrapped as we expect it in a lexically-scoped, expression-oriented language.</p><p>BLOCK / RETURN-FROM lets you do:</p><p></p><ul><li><b>Early returns</b> from functions or arbitrary code blocks</li><li><b>Breaking</b> from loops, including any number of nested loops</li><li><b>Continuing</b> in loops, including any number of nested loops</li><li>Even <b>arbitrary GOTOs</b> in a code block (with some macrology & trampolining, see <a href="https://www.plover.com/~mjd/misc/hbaker-archive/MetaCircular.html">Baker's TAGBODY</a>)</li></ul><p></p><p><b>(block name forms*)</b> lexically binds name within the forms as a non-local exit from which you can return a value with <b>(return-from name value)</b>. Just <b>(return-from name)</b> without a value uses nil as the value.</p><p>A BLOCK without any RETURN-FROM just returns the last value: </p><p><b>(block b 1 2 3)</b> returns 3.</p><p>This prints 1 and returns 2:</p><p><b>(block b (print 1) (return-from b 2) (print 3))</b></p><p>You can have any number of <b>nested blocks</b>:</p><p><b>(block b1 ... (block b2 ... (return-from b1) ...) ...)</b></p><p>To do an <b>early return</b> from a function, place a block at its beginning:</p><p><b>(defun foo ()</b></p><p><b> (block b </b></p><p><b> </b><b> </b><b>...</b></p><p><b> </b><b> </b><b>(return-from b)</b></p><p><b> </b><b>...))</b></p><p>(Common Lisp automatically places an anonymous block around every function body, so you don't need to do this in practice, but my hobby Lisp doesn't, and I'm using this explicit approach, and I like it.)</p><p>To <b>break from a loop</b>, place a block around it:</p><p><b>(block break</b></p><p><b> (loop</b></p><p><b> </b><b> </b><b>...</b></p><p><b> (return-from break)</b></p><p><b> </b><b>...))</b></p><p>To <b>continue in a loop</b>, place a block inside it:</p><p><b>(loop</b></p><p><b> (block continue</b></p><p><b> ...</b></p><p><b> (return-from continue)</b></p><p><b> ...))</b></p><p>You can have <b>multiple nested loops</b>, like in Java:</p><p><b>(block break-outer</b></p><p><b> (loop</b></p><p><b> (block break-inner</b></p><p><b> </b><b> </b><b> </b><b>(loop</b></p><p><b> </b><b> </b><b>...</b></p><p><b> (return-from break-inner)</b></p><p><b> ...))))</b></p><h3>UNWIND-PROTECT</h3><p style="text-align: left;"><a href="http://clhs.lisp.se/Body/s_unwind.htm">UNWIND-PROTECT</a> is effectively a try/finally block (without a catch).</p><div><b>(unwind-protect protected-form cleanup-forms*)</b> evaluates the protected form, and regardless of whether it returns normally or does a non-local exit with RETURN-FROM, the cleanup forms are evaluated.</div><div><br /></div><div><b>(unwind-protect (foo)</b></div><div><b> (bar)</b></div><div><b> </b><b>(quux))</b></div><div><br /></div><div>is analogous to</div><div><br /></div><div><b>try {</b></div><div><b> return foo();</b></div><div><b>} finally {</b></div><div><b> bar();</b></div><div><b> quux();</b></div><div><b>}</b></div><div><b><br /></b></div><div>Both of the following expressions print 2 and return 1:</div><div><b><br /></b></div><div><b>(unwind-protect 1</b></div><div><b> (print 2))</b></div><div><b><br /></b></div><div><b>(block exit</b></div><div><b> </b><b>(unwind-protect (return-from exit 1)</b></div><div><b> (print 2)))</b></div><div><b><br /></b></div><h3>Conclusion</h3><p style="text-align: left;">Common Lisp's BLOCK / RETURN-FROM and UNWIND-PROTECT offer a minimalistic and expressive system for non-local control flow.</p><div>(<a href="https://lobste.rs/s/ydeoe3/common_lisp_s_block_return_from_unwind">Lobsters</a>, <a href="https://news.ycombinator.com/item?id=39074568">HN</a>)</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-24327002484861156682016-07-12T18:30:00.001+02:002016-07-12T18:38:33.643+02:00Peeking into the future with RDPOne aspect of <a href="http://axisofeval.blogspot.com/2015/06/grokking-reactive-demand-programming.html">reactive demand programming</a> that sets it apart from other reactive programming models is its support for optimistically working with the predicted future states of signals.<br />
<br />
Think about a signal that carries the current time in seconds, that you want to display on screen as a clock. The screen should update close to the full second.<br />
<br />
Let's say your clock display uses complicated drawing routines that take a while to display. So if you start drawing at the full second, your drawing will always lag behind the full second.<br />
<br />
But why should this be? The clock signal already knows that it will change to the next full second at a particular time in the future, and can communicate this to the display code.<br />
<br />
<b>In RDP, we can view a value not as a point, but as an arrow in time, that may point into the future.</b> Consider a mouse: during mouse moves it is often possible to give a good prediction of where the mouse will move in the next instant. With RDP it's possible for the mouse signal to communicate this information to its clients.<br />
<br />
Given that the clock display code can peek into the clock signal's future, it can perform intensive processing at leisure before the actual switch to the next full second (e.g. by drawing into an offscreen buffer and copying it to the screen buffer at the full second).<br />
<br />
Predictions are often wrong, so clients always need to have a fallback (e.g. throwing away the prepared drawing in the offscreen buffer and drawing from scratch, accepting a laggy display). But the fact that RDP signals can inform clients about future states enables a wider spectrum of behaviors that were previously impossible to achieve.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-26809910744911384472016-05-19T22:54:00.000+02:002016-05-20T12:59:17.388+02:00The worm is the spiceJust had a nice insight regarding systems that deal with reactively updating (potentially <a href="http://axisofeval.blogspot.com/2015/08/restful-rdp-with-big-values.html">large, structured</a>) values such as <a href="http://axisofeval.blogspot.com/2015/06/grokking-reactive-demand-programming.html">RDP</a>:<br />
<br />
<i><b>The dynamic output of a process is indistinguishable from a static storage resource (file).</b></i><br />
<br />
In batch systems like Unix, this symmetry is not so deep: the output stream of a process looks <i>somewhat</i> like a file, but the process can't make any changes to earlier, already emitted portions of the stream. This is unlike a file, which can be edited anywhere.<br />
<br />
In a reactive system, a process output is truly indistinguishable from a file. The process may decide to update any part of the emitted value at any time, just like a user editing a storage resource.<br />
<br />
Apart from being a nice symmetry in itself, I think this also offers new possibilities for user experience. Processes and files can be used interchangeably in the UI. A file can be viewed as a (rather boring) process, whose output gets edited by the user (similar to data in pi calculus).Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-5722310642266356003.post-55171545211880347752015-08-02T15:42:00.001+02:002015-08-02T17:44:51.015+02:00RESTful RDP with big valuesWhat if you want to use a big value, like a whole database table or weblog, as a <a href="http://axisofeval.blogspot.com/2015/06/grokking-reactive-demand-programming.html">Reactive Demand Programming</a> signal value? This would make it possible to use RDP to orchestrate things like incremental MapReduce pipelines. Here's one weird trick to make it work.<br />
<br />
In effect, each RDP signal becomes a RESTful <i>server</i>, speaking an HTTP-like protocol. Clients of a signal remember the <a href="https://en.wikipedia.org/wiki/HTTP_ETag">ETag</a> of the last version of the signal they've processed, and send it to the server on subsequent requests.<br />
<br />
The protocol to retrieve the value of a signal may work like this:<br />
<ul>
<li>If a client hasn't seen a previous version of the signal, it sends no ETag. The server replies with the complete value. The value may be split up into multiple pages, using something like an Atom <a href="https://tools.ietf.org/html/rfc5005">paged feed</a>. (Other non-sequential kinds of splits are possible: for example, tree-like data like a filesystem can be fetched in parallel using hierarchical splits.)</li>
<li>If a client has seen and processed a previous version of the signal it sends the stored ETag. There are three possibilities:</li>
<ul>
<li>The content hasn't changed (i.e. the server's ETag matches the client-sent ETag), so the server replies with a status code that indicates that there is no new content (HTTP's 304 Not Modified).</li>
<li>The content has changed, so the server replies with a <i>diff</i> of the client's version versus the server's version.</li>
<li>The content has changed, but the server is unable to provide a diff against the client's version. This can happen for servers that do not keep a complete history, or also if the server determines that it's more efficient to have the client retrieve the whole value again instead of sending a diff. The client has to re-fetch the whole signal value, as if it had seen no previous version of the value.</li>
</ul>
</ul>
<div>
I haven't worked out all the details, but I think this scheme could be made to work.</div>
Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-5722310642266356003.post-45208708675595291972015-07-20T19:02:00.001+02:002016-07-03T20:15:35.624+02:00What I learned about Urbit so far[Updated, see <a href="http://axisofeval.blogspot.com/2015/07/what-i-learned-about-urbit-so-far.html?showComment=1437494795877#c71943271372764002">comment</a> and this <a href="https://www.reddit.com/r/urbit/comments/4okcm6/were_the_core_urbit_team_ask_us_anything_about/"><b>Reddit AMA with the Urbit team</b></a>, that clarifies a lot of issues.]<br />
<br />
<i><a href="http://urbit.org/">Urbit</a></i> is some kind of new operating system design thingy, that is kinda hard to categorize.<br />
<br />
Some interesting design points are:<br />
<ul>
<li>Urbit restricts the number of identities in the system to 2<sup>32</sup>. This means Urbit doesn't have enough identities even for currently living humans. In line with the usual obfuscation going on in Urbit, such an identity is called <a href="http://alexkrupp.typepad.com/sensemaking/2013/12/a-brief-introduction-to-urbit.html"><i>destroyer</i></a>.</li>
<li>Urbit is programmed in a weird programming language called Hoon. Hoon's (only) definition is a 10KLOC file <a href="https://github.com/cgyarvin/urbit/blob/master/aug/les/arvo/hoon.hoon">hoon.hoon</a>, written in itself. It uses practically random variable names (such as <i>nail</i> for "parse input", or <i>vase</i> for a "type-value pair"), not to speak of the "<i><a href="https://github.com/cgyarvin/urbit/blob/master/doc/book/0-intro.markdown#hoon">nearly 100 ASCII digraph 'runes'</a>"</i>. The author acknowledges that the parser is "very intricate".</li>
<li>Efficiency-wise, Hoon is impractical as a programming language, so in the real world, the VM will <a href="https://github.com/cgyarvin/urbit/blob/master/doc/book/0-intro.markdown#nock">recognize Hoon fragments</a>, and replace them with C code (called <i>jets</i>).</li>
</ul>
<div>
This brings us to the question: why would anybody actually design an OS like that? The best explanation I've seen so far is by Kragen, who writes:</div>
<blockquote class="twitter-tweet" lang="en">
<div dir="ltr" lang="en">
<a href="https://twitter.com/p_nathan">@p_nathan</a> <a href="https://twitter.com/msimoni">@msimoni</a> No, of course not. Urbit is designed to disempower users and make them subject to feudal overlords. Never read Moldbug?</div>
— kragen (@kragen) <a href="https://twitter.com/kragen/status/622939662072610816">July 20, 2015</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-5722310642266356003.post-41611846099090533212015-06-15T20:14:00.000+02:002015-06-27T23:33:42.774+02:00A trivial implementation of Reactive Demand ProgrammingI wrote a trivial implementation of <a href="http://axisofeval.blogspot.com/2015/06/grokking-reactive-demand-programming.html">RDP</a> in JavaScript to help me understand how it works.<br />
<br />
It's called <a href="https://github.com/manuel/bucky-rdp" style="font-style: italic; font-weight: bold;">bucky-rdp</a> (about 200 lines of heavily commented <a href="https://github.com/manuel/bucky-rdp/blob/master/rdp.js">code</a>).<br />
<br />
It currently supports <a href="https://github.com/dmbarbour/Sirea">Sirea</a>'s bconst, bpipe, and bfmap.<br />
<br />
Here's an example:<br />
<br />
<pre>// Create some behaviors for transforming numbers.
var bDouble = rdp.bFMap(function(val) { return val * 2; });
var bMinusOne = rdp.bFMap(function(val) { return val - 1; });
// Create a pipeline behavior of the two behaviors
var myBehavior = rdp.bPipe(bDouble, bMinusOne);
// Apply an inactive input signal to the pipeline behavior
var sigIn = rdp.makeSignal();
var sigOut = rdp.apply(myBehavior, sigIn);
// Change the input signal value and watch the output signal change
sigIn.setValue(2);
console.log(sigOut.getValue()); // Prints 3
sigIn.setValue(4);
console.log(sigOut.getValue()); // Prints 7
sigIn.deactivate();</pre>
<pre></pre>
<br />
(This post refers to <a href="https://github.com/manuel/bucky-rdp/tree/v1.0.1">v1.0.1</a>)Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-5722310642266356003.post-18324478559274447482015-06-09T15:10:00.000+02:002015-09-02T21:09:40.062+02:00Grokking Reactive Demand Programming<div>
<b>TL;DR:</b> RDP is an exciting declarative model of how computational processes (behaviors) are connected by continuously updating values (signals) to effect changes on storage and external state (resources).</div>
<div>
<br /></div>
I've come a bit closer to understanding <a href="https://awelonblue.wordpress.com/">David Barbour</a>'s <a href="https://github.com/dmbarbour/Sirea">Reactive Demand Programming</a> model, and this has confirmed my <a href="http://axisofeval.blogspot.com/2012/09/reactive-demand-programming.html">previous</a> hunch that RDP is one of the most interesting systems designs since Unix. If you're looking for new, better ways to structure dynamic, interactive applications, I strongly recommend checking out RDP.<br />
<div>
<br /></div>
<div>
I would call RDP an orchestration model, since it cares about how you connect and assemble components of your app, and gives you a lot of freedom in what these components do and how they interact. This also fits with David's description of an RDP application as "a complex symphony of signals and declarative effects orchestrated in space and time".<br />
<br />
In terms of Unix, RDP's behaviors correspond to processes, signals correspond to pipes, and resources correspond to storage and other external, stateful things.<br />
<br />
<b><span style="font-size: large;">
Signals (pipes, channels)</span></b><br />
<br />
A signal continuously delivers a potentially changing value. The <a href="https://github.com/dmbarbour/Sirea">current implementation</a> always updates the complete value, but RDP doesn't rule out diff/patch-based signal updates, to model e.g. a large set as a signal value.<br />
<br />
In addition to these simple signals carrying a single value, there are also compound signals, such as (x :&: y) which represents the concurrent, asynchronous product of signals x and y, IOW a signal representing two independently updating signals. Analogously, (x :|: y) represents a disjoint sum of signals, with either x or y being active at any given point in time.<br />
<div>
<br /></div>
<div>
A signal is either active (carrying a value), or inactive (disrupted). Application-level errors have to be modelled as part of the value, there is no "stderr".<br />
<br />
<b><span style="font-size: large;">
Behaviors (processes, computation)</span></b><br />
<br />
A behavior won't do anything until you place a demand on it. You place a demand on a behavior by applying an input signal (the demand) to it; the behavior will produce an output signal for the duration of this application.<br />
<br />
Multiple demands can be placed on a behavior at the same time. The behavior can either reply to each input signal with a different output signal, or with the same output signal, depending on the purpose of the behavior. For example, a "calculator" behavior may take N input signals with expressions like "1 + 2" and "3 * 5" and deliver a distinct output for each input; on the other hand, a "sum" behavior may take N input signals carrying a number and produce a total sum as the output signal, which would be the same for all inputs.<br />
<div>
<br /></div>
<div>
Behaviors can be composed into dataflow networks. A simple composition is the pipeline behavior, b1 >>> b2: the input signal of this pipeline behavior will be processed by the behavior b1; b1's output signal becomes the input signal for behavior b2; and finally, b2's output signal becomes the output of the whole pipeline behavior.</div>
<div>
<br /></div>
<div>
The <a href="https://github.com/dmbarbour/Sirea">Sirea</a> Haskell implementation of RDP comes with other behaviors such as bdup, that copies a single signal into both branches of a :&: product, for creating more complex networks of signals and behaviors. There are also primitives like bfirst and bsecond for running different behaviors against the branches of product signals, and bfmap for applying ordinary Haskell functions to signals. (See <a href="https://www.haskell.org/arrows/">Arrows</a>.)</div>
<div>
<b><br /></b>
<b><span style="font-size: large;">Resources (storage, external state)</span></b></div>
<div>
<br />
RDP doesn't say anything about state, so it has to come from the outside. Access to stateful resources such as filesystems is abstracted through behaviors: to access a filesystem you use a behavior like readFile "foo.txt" that continuously delivers the contents of the file "foo.txt" as output signal.</div>
</div>
<div>
<br /></div>
<div>
Creating new resources in RDP is impossible, so resource discovery idioms are used: for example, to "create" a file, you use a UUID as its name, and it will be automatically created the first time you write to it.</div>
<div>
<br /></div>
<div>
I hope this has been helpful. For further reading, check out the extensive <a href="https://github.com/dmbarbour/Sirea/blob/master/README.md">README</a> of the Sirea RDP implementation, and David Barbour's <a href="https://awelonblue.wordpress.com/">blog</a>.</div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-81317039320448241792014-06-22T19:54:00.001+02:002014-06-26T17:00:06.457+02:00Attack of the Monadic Morons[Deleted. To be replaced with a proper rant at another time.]Unknownnoreply@blogger.com16tag:blogger.com,1999:blog-5722310642266356003.post-9670543005915706172014-06-18T18:44:00.002+02:002014-06-18T18:45:47.769+02:00Obsession with mathematics<blockquote class="tr_bq">
<i>To put it bluntly, the discipline of programming languages has yet to get over its childish passion for mathematics and for purely theoretical and often highly ideological speculation, at the expense of historical research and collaboration with the other social sciences. PL researchers are all too often preoccupied with petty mathematical problems of interest only to themselves. <b>This obsession with mathematics is an easy way of acquiring the appearance of scientificity without having to answer the far more complex questions posed by the world we live in. </b></i></blockquote>
I've replaced "economics" with "programming languages" in this <a href="http://blogs.law.harvard.edu/philg/2014/06/17/book-review-pikettys-capital/">quote from Phil Greenspun's blog</a>. Seems appropriate, no?Unknownnoreply@blogger.com9tag:blogger.com,1999:blog-5722310642266356003.post-27322346948397216362013-06-11T01:08:00.000+02:002013-06-14T00:42:28.904+02:00A week of Lisp in Madrid<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.flickr.com/photos/cuellar/4670777817/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcx8XeLWDIfoDFIn3POEmh_7Ng4f-BSxSEYYGKgtdq51ttDwQZzgZfCnMcuug_n04ATVaVy87bTfVayDlRkjdd_Px02Zk4wDMXcNvmjMGYbVYBaaJc2Vgu0ik6GGda3HXfYtMTG7gt4j8/s320/4670777817_d657cd9819.jpg" width="320" /></a><a href="http://www.flickr.com/photos/cuellar/4670777817/"><span style="font-size: xx-small;"><i>Jose Maria Cuellar</i></span></a></div>
<br />
Geeks from all Lisps of life met in Madrid last week for the <a href="http://weitz.de/eclm2013/">European Common Lisp Meeting</a> and <a href="http://www-sop.inria.fr/members/Manuel.Serrano/conferences/els13.html">European Lisp Symposium</a> 2013.<br />
<br />
A lot of things happened, so I'll just recount the most memorable ones.<br />
<br />
I enjoyed meeting <a href="http://www.p-cos.net/">Pascal Costanza</a> and <a href="http://soft.vub.ac.be/soft/members/charlotteherzeel">Charlotte Herzeel</a>. Pascal shares my disdain for the aberration that is <a href="http://en.wikipedia.org/wiki/Lisp-1_vs._Lisp-2">Lisp-1</a> and doesn't tire of telling Schemers so. I think he's a bit too opposed to all things Scheme though, and when somebody tells him about the niceness that is, e.g. <a href="http://lambda-the-ultimate.org/node/4071">syntax-parse</a>, he goes all "nyah, nyah, nyah, I cannot hear you". Charlotte is one of the handful of people who understand 3-Lisp because she re-implemented it.<br />
<blockquote class="tr_bq">
<span style="font-size: x-small;">(Apparently, 3-Lisp is quite similar to <a href="http://web.cs.wpi.edu/~jshutt/kernel.html">Kernel</a>: every operative receives not only its operand tree and the lexical environment in which it is called, but also its continuation. I still don't understand it. Why would you pass the continuation to an operative, when it can easily obtain it using e.g. call/cc? Apparently because 3-Lisp considers the continuation to exist on the next meta-level, not the current one.)</span></blockquote>
<a href="http://www.ida.liu.se/~erisa/">Erik Sandewall</a>, who worked with John McCarthy, presented his document-editor/operating-system/database/agent-environment called <a href="http://www.ida.liu.se/ext/caisor/"><i>Leonardo</i></a>. We talked a lot about it, and I found out I'm working on a <i>very</i> similar system in my own next-gen OS efforts.<br />
<br />
A very enjoyable talk was <i><a href="http://en.wikipedia.org/wiki/Siscog">SISCOG</a>: a story written in Lisp</i>, by Tiago Maduro Dias and Luís Oliveira. SISCOG was started by two professor-level Lisp nerds in the eighties, because they wanted to apply the language to something useful. If you've ridden a train in Europe, chances are it was scheduled by one of SISCOG's apps. They employ a large number of Lisp programmers in Portugal, and reported how they view Lisp (most of them like it). Anyone who claims that dynamic languages can't be used for something other than prototyping I'd like to hit over the head with the SISCOG manual, which is probably heavy, given the highly complex stuff they work on.<br />
<br />
One of the absolute over-the-top experiences, both geek-wise, food-wise, and otherwise-wise was the extended dinner with Ludovic Courtès (<a href="http://www.gnu.org/software/guix/">Guix</a> and <a href="http://www.gnu.org/software/guile/">Guile</a>), <a href="http://wingolog.org/">Andy Wingo</a> (Guile and V8), <a href="http://florian.loitsch.com/">Florian Loitsch</a> (Hop and Dart), and <a href="http://www.ccs.neu.edu/home/samth/">Sam Tobin-Hochstadt</a> (Racketeer). It doesn't get much better for a PL geek than such a barrage of PL-nerdery, funny background stories, and Spanish cakes.<br />
<br />
Another memorable chat was with the very nice <a href="http://www.lukego.com/">Luke Gorrie</a> about dynlangs, networking, and immigration and property buying in the world's most agreeable country, Switzerland.<br />
<br />
I also enjoyed the discussions between Ernst "I'm very male" van Waning and Venetian gentleman <a href="http://januszpodrazik.com/">Janusz Prodrazik</a>, who presented OpusModus. <a href="http://opusmodus.com/">OpusModus</a> is a very polished tool for composers that uses S-expressions instead of scores for composition. Composers seem to enjoy it.<br />
<br />
The last, but not least, three days I enjoyed the company of style icon <a href="http://fare.tunes.org/">Faré</a> and Masatoshi Sano, who came all the way from Boston and Tokyo, respectively. Faré and I basically agree about all major points about how the next OS will look like, and I feel like I spiritually joined the <a href="http://tunes.org/">TUNES project</a> and made a friend. (Watch out, TUNES will be on your lap sooner than you imagine!)<br />
<br />
Greetings to Piotr Kuchta with whom I immediately hit it off, who <a href="https://github.com/piokuc/pywat">ported Wat to Python</a>, and who I look forward to visiting in rainy GB.<br />
<br />
All in all, a memorable and enjoyable week in the beautiful city of Madrid. Thanks to the organizers for the flawless execution and see you all again next time!Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-5722310642266356003.post-26604665363294153282013-05-29T00:07:00.000+02:002013-12-21T00:38:11.463+01:00Wat: now in Perl, Python, and Ruby, tooI'm delighted somebody by the name of "shadowcat-mst" has taken my <a href="https://github.com/manuel/wat-js">Wat</a> interpreter and <a href="https://github.com/shadowcat-mst/wat-pl/">reimplemented it in Perl</a>. That really seems fitting. Wat now covers JavaScript and Perl - think of the possibilities!<br />
<br />
<b>Update:</b> Piotr Kuchta <a href="https://github.com/piokuc/pywat">ported Wat to Python</a>.
<br />
<b>Update:</b> Victor Hugo Borja <a href="https://github.com/vic/wat.rb">ported Wat to Ruby</a>.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-7246854296760400142013-05-09T19:35:00.000+02:002016-05-03T22:15:53.020+02:00Green threads in the browser in 20 lines of Wat<a href="http://manuel.github.io/wat-js/doc/demos/sleeping.html"><b>This page</b></a> shows 5 independent, cooperatively scheduled <a href="https://github.com/manuel/wat-js">Wat</a> green threads (view source for full Wat code).<br />
<br />
Each thread has an ID and is defined as a function that loops forever, repeatedly printing its ID, and then sleeping for a (randomly long) while.<br />
<pre>(define (run-thread (id String))
(loop
(@appendChild (.body $document)
(@createTextNode $document (+ "Active thread: " id " ")))
(sleep (* 1000 (@random $Math)))))</pre>
So, how can a Wat thread sleep inside a loop when JavaScript forbids sleeping? Why, with good ole <a href="http://axisofeval.blogspot.com/2011/08/notes-on-delimited-continuations.html">delimited continuations</a>:<br /><br />
To spawn a thread, I just wrap the call to RUN-THREAD in a prompt (which is just a unique object used as an identifier):
<br />
<pre>(define default-prompt 'default-prompt)
(define (spawn-thread (id String))
(push-prompt default-prompt
(run-thread id)))
</pre>
Where it gets interesting is the SLEEP function which captures the continuation up to the prompt, and sets up a callback with JavaScript's setTimeout that will reinstall the continuation later:<br />
<pre>(define (sleep (ms Number))
(take-subcont default-prompt k
(define (callback . #ignore)
(push-prompt-subcont default-prompt k))
($setTimeout (js-callback callback) ms)))
</pre>
So, first, SLEEP aborts up to and including the default prompt using TAKE-SUBCONT. It receives the continuation in the variable K. Once it has K, it defines a CALLBACK function, that will reinstall the default prompt with PUSH-PROMPT, and then continue with K again with PUSH-SUBCONT. All that's left is to give this callback to setTimeout.<br />
<br />
Then I can spawn independent threads:<br />
<pre>(spawn-thread "thread-1")
(spawn-thread "thread-2")
(spawn-thread "thread-3")
(spawn-thread "thread-4")
(spawn-thread "thread-5")
</pre>
Wat is very new, but it's already practical for adding concurrency and metaprogramming to JavaScript. Deployment is very easy. Include the single <a href="https://github.com/manuel/wat-js/blob/master/wat.js">wat.js</a> file, put some Lisp code in a <script> tag, and run it.</p>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-5722310642266356003.post-12910138422596692952013-05-08T18:35:00.001+02:002013-05-10T01:35:15.674+02:00A new low in programming language design and implementation<div>
The new <a href="https://github.com/manuel/wat-js"><b><i>Wat</i></b></a> is the best, most lightweight way to implement a JavaScript-based programming language I have found so far.</div>
<div>
<br /></div>
<div>
Basically, I get away from JS as quickly and painlessly as possible, and start writing the language in itself.</div>
<div>
<br /></div>
<div>
So I define a very small set of primitives on the joint foundation of Kernel-like <a href="http://web.cs.wpi.edu/~jshutt/kernel.html">first-class lexical environments and fexprs</a> and <a href="http://www.cs.indiana.edu/~dyb/pubs/monadicDC.pdf">delimited continuations</a>. Fexprs are a great tool for language-oriented programming, and delimited continuations allow me to escape from the browser's (and Node's) async hell and implement any concurrency and effect system I like.</div>
<div>
<br /></div>
<div>
To fexprs I also add macros. When a macro is used as the operator of a form, the form's code gets changed to the macro's output when the macro is first called, a technique I learned from <a href="http://people.csail.mit.edu/jaffer/CNS/interpreter-latency">here</a>. I like macros because they make syntactic abstraction cost-free - with fexprs alone there is always an interpretative overhead. Still, Wat macros, like fexprs, do not work with quoted identifiers, but with first-class values, so many hygiene problems are avoided.</div>
<div>
<br /></div>
<div>
To delimited control I also add classic first-order control (sequential, conditional, loop, throw, catch, finally). This runs on the ordinary JS stack. Only when a continuation is captured does the stack get reified on the heap.</div>
<div>
<br /></div>
<div>
And last but not least, I use <a href="https://github.com/manuel/wat-js/blob/f84023f6581fec664a66b2512d13d97224283e75/wat.js#L384">a JSON-based syntax for writing the language in itself</a>. At first this was just intended as a quick way to not have to specify a parser for Wat, but I'm starting to like it. It allows Wat to truly be embedded in JavaScript.</div>
<div>
<br /></div>
<div>
Wat does not have a type tagging or object system. It uses the raw JavaScript values.</div>
<div>
<br /></div>
<div>
The whole implementation is roughly 350 lines of JavaScript. After these 350 lines, I can already <a href="https://github.com/manuel/wat-js/blob/f84023f6581fec664a66b2512d13d97224283e75/wat.js#L384">write Wat in Wat</a>, which is just great.</div>
Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-5722310642266356003.post-81795010592252137492013-05-05T01:17:00.000+02:002013-05-05T03:08:46.455+02:00Some progress on the Wat VM<a href="https://github.com/manuel/wat-js"><i>Wat</i></a> is back! If you'll recall, Wat is my ultra-minimal (~500 lines of JS) interpreter for a <a href="http://web.cs.wpi.edu/~jshutt/kernel.html">Kernel</a>-based language with <a href="http://axisofeval.blogspot.com/2012/09/mixing-first-order-and-higher-order.html">delimited continuations as well as first-order control</a>, and <a href="http://axisofeval.blogspot.com/2012/09/having-both-fexprs-and-macros.html">hygienic macros as well as fexprs</a>.<br />
<br />
I'm pretty excited by some recent and ongoing changes, which make Wat even smaller and turn it into more of a VM than a full language. Wat will provide (just) the following features:<br />
<ul>
<li><a href="http://axisofeval.blogspot.com/2011/08/notes-on-delimited-continuations.html">delimited continuations</a> and <a href="http://okmij.org/ftp/Computation/dynamic-binding.html">delimited dynamic binding</a> (higher-order control); these will be used to build cooperative multithreading with thread-local bindings</li>
<li><a href="http://axisofeval.blogspot.com/2012/09/mixing-first-order-and-higher-order.html">try/catch/finally</a> (first-order control) integrated with the JS stack, but suspendable by continuation capture</li>
<li>fexprs as well as in-source self-modifying-code <a href="http://people.csail.mit.edu/jaffer/CNS/interpreter-latency">memoizing macros</a> (which are hygienic, as they're built on Kernel)</li>
<li>a native JS interface</li>
</ul>
<div>
And that's about it. This should give an extremely minimal yet powerful infrastructure for building JavaScript-based languages.</div>
<div>
<br /></div>
<div>
And I gave up <a href="http://axisofeval.blogspot.com/2013/04/a-quasiquote-i-can-understand.html">on quasiquotation</a> and Scheme-style <a href="http://axisofeval.blogspot.com/2010/05/understanding-hygiene-part-1.html">hygienic macros</a> again. I just cannot get them to work in a satisfying manner.</div>
<div>
<br /></div>
<div>
Exempli gratia, here's some <a href="https://github.com/manuel/wat-js/blob/master/virtua.wat">initial Wat VM "microcode"</a> for bootstrapping a vaporlanguage.</div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-5722310642266356003.post-58966126856240426872013-04-28T19:20:00.001+02:002013-04-28T19:57:35.517+02:00A quasiquote I can understandI've written two Lisps (<a href="https://github.com/manuel/ell">1</a>, <a href="https://github.com/manuel/edgelisp">2</a>) with <a href="http://people.csail.mit.edu/alan/ftp/pepm99.ps.gz">quasiquotation</a>, and in both, quasiquotation was the most difficult thing to implement, and gave me the most headaches. That shouldn't be, right? After all, it only creates new forms.<br />
<br />
I think now I've found a formulation for quasiquote that has a really simple implementation, and yields more or less the same results as existing quasiquote implementations.<br />
<br />
Some definitions:<br />
<ul>
<li>`foo stands for (quasiquote foo), `(foo) stands for (quasiquote (foo)).</li>
<li>,foo stands for (unquote foo) and is only allowed within a quasiquote.</li>
<li>,@foo stands for (unquote-splicing foo) and is only allowed within a quasiquote.</li>
<li>Quasiquote, unquote, and unquote-splicing only ever take a single operand.</li>
<li>`foo = 'foo, i.e. a quasiquoted symbol yields simply the symbol.</li>
<li>`"foo" = "foo", `12 = 12, and likewise for other literals.</li>
</ul>
<div>
The main difficulty I previously had with quasiquote came from unquote-splicing, which inserts a list of multiple elements into the constructed list (whereas nested quoted or unquoted forms only insert a single element). The main idea in this new formulation is to make inserting multiple elements the default, and define nested quoted or unquoted elements as simply inserting a list containing a single element.</div>
<div>
<br /></div>
<div>
Every `(...) expression therefore stands for an <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_append.htm">APPEND</a> of the list's further processed elements.</div>
<div>
<br /></div>
<div>
For example, given</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">(define foo 1)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">(define bar 2)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">(define quux '(3 4))</span></div>
<div>
<br /></div>
<div>
the quasiquote expression</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">`(foo ,bar ,@quux)</span></div>
<div>
<br /></div>
<div>
stands for</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">(append (list 'foo) (list bar) quux)</span></div>
<div>
<br /></div>
<div>
which produces the following when evaluated:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">'(foo 2 3 4)</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
So, processing a quasiquoted list works by wrapping each element, except for unquote-splicing forms, in a call to LIST, and APPENDing the results. Quoted elements (foo) get processed recursively. Unquoted elements (bar) are passed to the call to LIST unprocessed. Unquote-splicing forms (quux) are inserted directly into the APPEND form.</div>
<div>
<br /></div>
<div>
I haven't implemented this yet, but I think defining a quasiquoted list `(...) as an APPEND really simplifies things.</div>
Unknownnoreply@blogger.com8tag:blogger.com,1999:blog-5722310642266356003.post-39080439812258982592013-02-01T16:16:00.000+01:002013-02-09T18:18:39.346+01:00Taf's translation to O'Caml for type-checking<a href="https://github.com/manuel/taf">Taf</a> is my new vapor-Lisp with row polymorphism, delimited continuations, and hygienic macros.<br />
<br />
(Warning: incoherent rambling ahead!) Taf has a class-based object system with no inheritance. A class defines which slots an instance of this class has, and which methods are applicable to it. Every class also implicitly defines a class type. In addition to class types, there are also interface types or simply interfaces. An interface type defines a suite of methods applicable to objects of this type. Everything is an object of a single class, but may have many compatible class types and interface types. Every object is a member of a special top type. There is no implicit subtyping: objects need to be upcast to top.<br />
<br />
All Taf objects are encoded as O'Caml objects. There is one O'Caml class for each Taf class. All classes inherit from a top class. Interfaces (method suites) are also defined as O'Caml classes. Any object can be statically <i><b>upcast</b></i> to top or any of the interfaces it implements. This is structural: an object can be upcast to an interface if it has all its methods. Objects have full <i><b>RTTI</b></i>, so they can also be dynamically <i><b>downcast</b></i>, resulting in an exception if the object is not of the given type. (A more convenient TYPECASE is provided as a wrapper.) Internally, downcasting is implemented via <i><b>Obj.magic</b></i> on the O'Caml side, and via a <b><i>dynamic type-check</i></b> in the VM. So Taf supports for example <i><b>heterogenous containers</b></i> containing arbitrary instances of top or of any other type. Any object can be put into the container, and taken out again, and cast back to its original class type. Likewise, it's possible to write methods that operate on objects of arbitrary types, such as Java's EQUALS. Types are <i><b>parametric</b></i>.<br />
<br />
Another aspect is the semantics of the <b><i>global environment</i></b>. O'Caml's is basically that of a LET* for values, and LETREC only for groups of functions. But Lisp requires LETREC* for all values. So every binding must be encoded as a reference cell containing a Maybe on the O'Caml side, to model bindings which may be undefined.<br />
<br />
The runtime, and also the code that produces O'Caml code will run in the browser. Eventually, the type-checker will be implemented in the language itself, so O'Caml will no longer be needed.<br />
<br />
Update: here's a sneak preview of the <a href="http://manuel.github.com/taf/doc/manual.html">Taf Language Manual</a>.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-5722310642266356003.post-46042003849325238922013-01-14T22:24:00.004+01:002013-01-14T22:24:51.429+01:00Current projectIn my quest for a good Lisp, I could no longer ignore static types.<br />
<br />
See <a href="http://manuel.github.com/taf/doc/plan.html"><i><b>Taf - A plan for a statically-typed Lisp</b></i></a>.<br />
<br />
There shouldn't be any difficult roadblocks, so I expect a release sometime in or before summer.Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-5722310642266356003.post-65871616549245509122012-11-24T14:19:00.001+01:002012-11-28T02:38:08.211+01:00This PLT Life moved<a href="http://this-plt-life.tumblr.com/"><b><i><span style="font-size: x-large;">This PLT Life is now on Tumblr</span></i></b></a>, thanks to <a href="http://www.bloggertotumblr.com/">bloggertotumblr.com</a>.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-36103490358512194192012-11-19T23:29:00.000+01:002012-11-19T23:29:33.080+01:00When I see three layers of weird DSLs for configuring a single app<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdHp1ynC9RdR69sLXqZxL0KdOlGTEbKWavOsGUuBo4hpf64HugW76YyfyTBuS-5BMMqusQp2fRXKnOwiQKzjMo9WpHGhWI1pVXeQUMARcXQHyfuhQEFKhgW6WqXpT4oUCqS9W8oWOQri8/s1600/dsl.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdHp1ynC9RdR69sLXqZxL0KdOlGTEbKWavOsGUuBo4hpf64HugW76YyfyTBuS-5BMMqusQp2fRXKnOwiQKzjMo9WpHGhWI1pVXeQUMARcXQHyfuhQEFKhgW6WqXpT4oUCqS9W8oWOQri8/s1600/dsl.gif" /></a></div>
<div class="separator" style="clear: both; text-align: right;">
<span style="font-size: xx-small;">(Hello, Jetty.)</span></div>
Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-5722310642266356003.post-21911388309623268782012-11-15T17:50:00.002+01:002012-11-15T17:50:24.994+01:00When everybody and their dog writes a poor OOP critique<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfzgHbAV0tNEqb_CWwYtwiQIs5XRcoMCmmcKYLltLK0-ZB8AgshwmD7u4krwVkENSmaZ7XNHQf1UTuKPG9CQhthhI7n2vswyCMxPzKk5Y3k5vpCBZPZhR4T3w1Hlehr3gH829h_4gRH7k/s1600/oop.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfzgHbAV0tNEqb_CWwYtwiQIs5XRcoMCmmcKYLltLK0-ZB8AgshwmD7u4krwVkENSmaZ7XNHQf1UTuKPG9CQhthhI7n2vswyCMxPzKk5Y3k5vpCBZPZhR4T3w1Hlehr3gH829h_4gRH7k/s320/oop.gif" width="320" /></a></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-21778615901929023982012-11-14T22:36:00.000+01:002012-11-14T22:36:05.908+01:00When I read a discussion about Scheme fundamentals on the RnRS editors list<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqLUmRgpvTjb-dpMrNwO3PJy6ytog8g5DRLBklWvbEGwXfnqjrlGU4p0LO7nAnhmjuGlg4biC0VxD33EfQzQZQeMNgzRfSWeMuNW6A_vwcgVaKZ144X42Lt_qCVh46k9KzXXbkbnG_xIw/s1600/rnrs.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqLUmRgpvTjb-dpMrNwO3PJy6ytog8g5DRLBklWvbEGwXfnqjrlGU4p0LO7nAnhmjuGlg4biC0VxD33EfQzQZQeMNgzRfSWeMuNW6A_vwcgVaKZ144X42Lt_qCVh46k9KzXXbkbnG_xIw/s320/rnrs.gif" width="320" /></a></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-16206535571246481012012-11-09T14:16:00.000+01:002012-11-09T14:18:35.027+01:00When the designers of a bad language decide to add a poorly-understood, "powerful" new feature<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHAMytgvC2qPJJviFfTzUfma0ioATCv_ohZWYM2EH-OsL2MRP16-ByxYPf6rIcHJDzBe2AIQkIbFFTjRzRuCZVdyuhjFEA4TBnx0I5GIkTcurNSp_yQPJFDkAE8SDo28cY6CljxGZj0Tc/s1600/js.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHAMytgvC2qPJJviFfTzUfma0ioATCv_ohZWYM2EH-OsL2MRP16-ByxYPf6rIcHJDzBe2AIQkIbFFTjRzRuCZVdyuhjFEA4TBnx0I5GIkTcurNSp_yQPJFDkAE8SDo28cY6CljxGZj0Tc/s1600/js.gif" /></a></div>
Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-5722310642266356003.post-2548181206124383892012-11-09T14:02:00.000+01:002012-11-09T14:02:13.513+01:00When someone shows me their implementation of McCarthy's EVAL<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6KrC42BKGlKIprSIB2lDT2akSGeorAjuoOWLdDzre1ud1zV-1ZZF6x0TK7qt3ipAfK2CQW2ybloWvpkKLdjcRyw9bVDqR1W0jlRulhg2GFHOpMB-6Dl5B0cK5HPYZqw5IYzt8gWFpMo4/s1600/chowyunfat.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6KrC42BKGlKIprSIB2lDT2akSGeorAjuoOWLdDzre1ud1zV-1ZZF6x0TK7qt3ipAfK2CQW2ybloWvpkKLdjcRyw9bVDqR1W0jlRulhg2GFHOpMB-6Dl5B0cK5HPYZqw5IYzt8gWFpMo4/s1600/chowyunfat.gif" /></a></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5722310642266356003.post-88530574119481152512012-11-08T18:37:00.000+01:002012-11-08T18:41:39.419+01:00When I listen to someone who knows less about PLs than me<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5iY6MidfNKSAqKmk3pJS9rYHAVOcPguSd5DNjFbl1eaI6V3zI8fMyahrfEM5vjchlxmm35RPpxfiMfMDTW2hhVtmDodgPSmo1yweZS9-I23B0QyohSEfkoP3uh9JZEIIiKMb92z1NUXg/s1600/plt.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5iY6MidfNKSAqKmk3pJS9rYHAVOcPguSd5DNjFbl1eaI6V3zI8fMyahrfEM5vjchlxmm35RPpxfiMfMDTW2hhVtmDodgPSmo1yweZS9-I23B0QyohSEfkoP3uh9JZEIIiKMb92z1NUXg/s1600/plt.gif" /></a></div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-5722310642266356003.post-20303074949371585692012-11-08T18:29:00.002+01:002012-11-08T18:29:40.788+01:00When I hear of a LISP success in industry<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXavqyuiikGO1pNuxjczBtRwYWPnOaT80KsggLPlaEpS2COzj4nEjk-WlLCDtnWBwUZwl6q1RwPelK4k7nyBalrfeIwFq3P3pyWNjaN69WMYym0g1VR806d_YkjW8Q08qQ5oBGv_Y9DW0/s1600/lisp.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="239" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXavqyuiikGO1pNuxjczBtRwYWPnOaT80KsggLPlaEpS2COzj4nEjk-WlLCDtnWBwUZwl6q1RwPelK4k7nyBalrfeIwFq3P3pyWNjaN69WMYym0g1VR806d_YkjW8Q08qQ5oBGv_Y9DW0/s320/lisp.gif" width="320" /></a></div>
Unknownnoreply@blogger.com0