0% found this document useful (0 votes)
145 views6 pages

JACL: A Common Lisp For Developing Single-Page Web Applications - Alan Dipert

Alan Dipert

Uploaded by

papachan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
145 views6 pages

JACL: A Common Lisp For Developing Single-Page Web Applications - Alan Dipert

Alan Dipert

Uploaded by

papachan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

JACL: A Common Lisp for Developing Single-Page Web

Applications
Alan Dipert
[email protected]

ABSTRACT by developers. The JACL language is envisioned as the means to


This paper introduces JavaScript-Assisted Common Lisp (JACL), that goal.
a new Web-browser based implementation of an extended subset Most popular, contemporary compile-to-JavaScript languages
of Common Lisp. JACL — which is under active development and are oriented around the affordances of static type checking; JACL,
approaching utility — is an effort to facilitate the use of Common as a Lisp, is not. Compared to similar languages that are Lisps, JACL
Lisp in overcoming the challenges of Single-page Web Application differentiates itself in two particular ways: with its approach to the
(SPA) development. JACL promotes interactive development in the REPL, and with its compilation techniques.
Web browser environment with its asynchronous reader and Chrome
DevTools-based REPL client. JACL also includes an optimizing Lisp- 2 RELATED WORK
to-JavaScript compiler capable of generating competitively small Many Lisps exist that either compile to JavaScript or are interpreted
and efficient JavaScript. by a JavaScript program. Here, only Lisps that have either demon-
strated industrial utility or that implement a significant subset of
CCS CONCEPTS Common Lisp are featured.
• Software and its engineering → Dynamic compilers; Run-
time environments. 2.1 Parenscript
Released in 2005[1], Parenscript[17] was the first Common Lisp
KEYWORDS compiler to target JavaScript. Parenscript is not bootstrapped and
Common Lisp, JavaScript, web applications its compiler is not written in JavaScript, and so it relies on a hosting
Common Lisp system for compilation. Only JavaScript types are
ACM Reference Format:
available to Parenscript programs at runtime, and so Parenscript is
Alan Dipert. 2020. JACL: A Common Lisp for Developing Single-Page Web
more of a syntax frontend for JavaScript than it is an interactive
Applications. In ELS ’20: European Lisp Symposium, April 27–28, 2020, Zürich,
Switzerland. ACM, New York, NY, USA, 6 pages. https://doi.org/10.1145/ Lisp system. While Parenscript is not positioned to facilitate large-
1122445.1122456 scale SPA development, it remains a popular way to add dynamic
JavaScript-based behaviors to static Web sites.
1 INTRODUCTION
2.2 SLip
The demand for SPAs in the past decade has only grown, and users
and stakeholders continually expect larger and more sophisticated SLip[2, 3] is arguably the most ambitious Common Lisp-on-JavaScript
applications. Unfortunately, large-scale development on the Web system created to date, even though it intentionally diverges[4]
browser platform presents a particular set of challenges that are not from Common Lisp in certain ways. It offers a stunning array of
easily overcome. Developers have responded to these challenges powerful features including a self-hosting compiler, a full set of con-
by creating a widening variety of special-purpose programming trol operators, JavaScript Foreign-Function Interface (FFI), tail-call
languages that compile to JavaScript [11, 27, 29]. Each new language optimization, green threads, and perhaps most impressively, a resi-
promotes one or more paradigms, application architectures, or dent Emacs clone, Ymacs. SLip is based originally on the compiler
development workflows, and claims some advantage relative to the and bytecode interpreter presented in Chapter 23 of Paradigms of Ar-
status quo. tificial Intelligence Programming: Case studies in Common Lisp[22].
This paper presents one new such language, JavaScript-Assisted Lisp files may be batch-compiled to FASLs. FASLs represent code
Common Lisp (JACL), an implementation of an extended subset as JavaScript code instead of as Lisp data. The browser is able to
of Common Lisp. The primary goal of the JACL project is to ease load FASLs faster than SLip code because the JavaScript parser
SPA development by applying Common Lisp — a proven[6, 12, 14] in the browser is much faster than the SLip reader. Despite the
substrate for UI innovation — to the difficult challenges now faced ability to produce FASLs, the interpreted nature of SLip precludes
the system from producing competitively fast or small application
Permission to make digital or hard copies of all or part of this work for personal or deliverables. Consequently, SLip does not satisfy the JACL project
classroom use is granted without fee provided that copies are not made or distributed
for profit or commercial advantage and that copies bear this notice and the full citation goal of facilitating large-scale industrial SPA development.
on the first page. Copyrights for components of this work owned by others than ACM
must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, 2.3 JSCL
to post on servers or to redistribute to lists, requires prior specific permission and/or a
fee. Request permissions from [email protected]. Of existing Common Lisps, JSCL[23, 24] is the one aligned most
ELS ’20, April 27–28, 2020, Zürich, Switzerland closely with the JACL project goal. Unlike Parenscript, JSCL is a
© 2020 Association for Computing Machinery.
ACM ISBN 978-1-4503-XXXX-X/18/06. . . $15.00 genuine Lisp system. And unlike SLip, JSCL compiles directly to
https://doi.org/10.1145/1122445.1122456 JavaScript instead of to an interpreted bytecode. It is self-hosting,
ELS ’20, April 27–28, 2020, Zürich, Switzerland Alan Dipert

includes the major control operators, and integrates tightly with 2.4 ClojureScript
JavaScript. JSCL includes a reader, compiler, and printer, and evalua- A discussion of industrial Lisp technology in the SPA setting would
tion is performed by the JavaScript eval() function. Between these, be incomplete without mention of ClojureScript [9]. ClojureScript
a Read Eval Print Loop (REPL) is possible, and the JSCL distribution is probably the most successful Lisp dialect for building SPAs by
includes an implementation of one. number of commercial users [10].
ClojureScript targets JavaScript, and is a dialect of an earlier
2.3.1 Synchronous reader. JSCL supports reading from string-backed
language, Clojure[8], which targets Java Virtual Machine (JVM)
pseudo-streams. Input streams from which characters may be syn-
bytecode. The ClojureScript reader and macro systems were both
chronously consumed are not an abstraction supported by JavaScript
originally hosted in Clojure, in a manner similar to Parenscript.
in Web browsers. With a few obscure exceptions1 , JavaScript pro-
Since its introduction[15], ClojureScript has heavily promoted
grams may only receive input asynchronously. An error is signaled
and prioritized the ability to produce high-performance deliverables.
if the end of a string-backed input stream is encountered before the
It has always been capable of generating JavaScript deliverables
reader has finished reading a datum.
amenable to aggressive optimization by the Google Closure Com-
Because input strings may not contain partial data, the REPL
piler. In this respect, ClojureScript aligns closely with the JACL
necessitates a “pre”-reader process that accumulates characters in
project goal of competitive application performance. In fact, ex-
response to asynchronous input events, and invokes the reader
perience with, and admiration for, ClojureScript is the reason the
only once a complete form — as a string — has been accumulated.
ability to produce high-performance deliverables is considered a
Such a pre-reader can be found in the JSCL REPL implementation.
crucial capability of JACL.
It handles standard syntax, but has the potential to be stymied by
Other than the fact that JACL is a Common Lisp and Clojure-
extended syntax, such as that added by reader macros. As such,
Script is not, the biggest difference between the two is that JACL
the pre-reader is a separate, degenerate reader that limits what’s
promotes a browser-based development environment with mini-
capable of being read by the underlying, full-featured synchronous
mal host-side tooling. ClojureScript, in contrast, promotes[21] a
reader.
development experience oriented around compilation performed
2.3.2 Compiler organization. JSCL compilation is performed in on the host.
two stages:
• Conversion from Lisp to a JavaScript Abstract Syntax Tree 3 DESIGN AND IMPLEMENTATION
(AST) represented as S-expressions. From a design perspective, JACL is an effort to balance the require-
• Conversion from JavaScript AST to JavaScript strings. Some ments of an interactive and practical Lisp development environ-
code-size optimization of arithmetic expressions is also per- ment with the constraints imposed by the Web browser platform.
formed in this stage. JACL proposes several innovations with respect to previous work
The first stage, the conversion from Lisp to JavaScript Abstract in pursuit of this balance.
Syntax Trees (AST), is where the implementation of the Lisp spe-
cial forms in terms of JavaScript language constructs and runtime 3.1 Asynchronous reader
support is performed. This conversion is done in a single pass The basis for interactive development in Lisp is undeniably the
in which macro expansion, lexical analysis, and JavaScript AST REPL, but as the JSCL “pre-reader” demonstrates, even the direct
generation all occur. The lexical environment is maintained in a approach to this simple mechanism is hampered by the asynchro-
dynamically-scoped variable as the compiler descends into Lisp nous model of input imposed by JavaScript.[19]. Traditionally, Lisp
code and produces JavaScript AST. readers are implemented in environments with a blocking function
Code for TAGBODY is generated in the first stage, and the gener- for obtaining input, like getc(1) on Unix. The blocking nature of
ated code is much slower than comparable JavaScript code. Every input consumption allows the reader to consume nested input recur-
control transfer initiated by GO results in a JavaScript exception be- sively, using the call stack to accumulate structures. In JavaScript,
ing thrown, which is an expensive operation. Since many Common input arrives asynchronously, and only when the call stack is empty.
Lisp operators have implicit tagbodies, and since most other itera- To mitigate this difficulty, the JACL reader facility is completely
tion operators are expressed in terms of TAGBODY, this performance asynchronous. Conceptually, it is the JSCL REPL “pre-reader” taken
problem pervades the JSCL system. to its inevitable conclusion.
More efficient ways of implementing TAGBODY are not hard to The JACL reader is implemented as a JavaScript class, Reader.
imagine, but the JSCL compiler does not amene itself to the imple- Reader instances are parameterized by an input source. One such
mentation of this or other high-level optimizations, as JSCL lacks input source is the BufferedStream class. The input source asyn-
a sufficiently expressive intermediate representation (IR). High- chronously notifies the reader instance when characters are avail-
level optimizations and transformations are important for a Lisp to able. The reader incrementally consumes these characters. Once
JavaScript compiler to perform, because they are exactly the kind the reader has accumulated a Lisp datum, it notifies its subscribers
that a JavaScript optimizer could not later perform, provided only of the availability of the datum.
JavaScript code. The JACL reader implementation makes extensive use of modern
JavaScript features to support asynchronous programming includ-
1 window.prompt() and window.confirm() are two JavaScript functions that may be ing promises, iterators, async functions, async iterators, and the
used to synchronously query the user for input. await keyword. These features simplify the JACL implementation
JACL: A Common Lisp for Developing Single-Page Web Applications ELS ’20, April 27–28, 2020, Zürich, Switzerland

and aid its performance [18]. It is hoped that JACL will eventually However, most developers already have a preferred text editor
be written in itself, and that these features will be accessible from and a REPL interaction workflow, and so it’s not within the JACL
Lisp, perhaps as a set of implementation-dependent declaration project scope to build a resident IDE in the style of SLip. Even if a
specifiers available in DECLARE expressions. resident IDE was a goal, the file system access restrictions imposed
The following example demonstrates, in JavaScript, the process by the browser would present significant challenges.
by which the JACL reader consumes characters and produces Lisp Instead, JACL offers an alternative development REPL approach
objects. A BufferedStream and Reader are instantiated, sent char- that requires minimal host tooling: the DevTools-based REPL. Google
acters asynchronously, and then the resulting Lisp object is printed Chrome is capable of hosting a WebSocket-based debug server that
to the JavaScript console. implements the DevTools Protocol [13]. DevTools Protocol clients
may then connect to the server and interact with open tabs, such
(async () => { as by evaluating arbitrary JavaScript within the context of the tab.
let bs = new BufferedStream(), JACL leverages the DevTools Protocol to deliver a command-line
rdr = new Reader(bs); REPL client that may be run on development machines. The work-
flow is the following:
window.setTimeout(() => bs.write("1"), 1000);
(1) Run Google Chrome from the shell with the
window.setTimeout(() => bs.write("2"), 2000);
–remote-debugging-port parameter.
window.setTimeout(() => bs.write("3"), 3000);
(2) Navigate to the Web site hosting the JACL system you wish
window.setTimeout(() => bs.write(" "), 4000);
to interact with.
(3) Run jacl-repl from the shell.
console.log(await rdr.read());
(4) Be presented with a Lisp prompt.
})();
As a simple command-line application with a textual interface,
In the preceding example, window.setTimeout() is used to en- jacl-repl can be run in various contexts. For example, it could be
queue several JavaScript functions for execution after 1000, 2000, run within an Emacs “inferior-lisp” buffer, and then Lisp forms could
3000, and 4000 milliseconds. Each enqueued function writes a char- be sent from other Emacs buffers for evaluation in the REPL. It could
acter of input to the BufferedStream bs when invoked. also be run as part of a build process that pipes Lisp sources over the
Before any enqueued function is invoked, execution proceeds to WebSocket for batch compilation. It is anticipated that additional
the console.log call, but is suspended by the await keyword. host-side tools that depend on jacl-client will be necessary in
The await keyword expects a JavaScript Promise object on its the future to support loading source files in dependency order.
right side, and JavaScript execution remains suspended until the Unlike the pre-readers of SLip and JSCL, jacl-client is com-
Promise has “resolved”, or notified its subscribers that the pending pletely ignorant of Lisp syntax. jacl-client merely transports
computation it represents has completed. rdr.read() is an async characters between the host machine and the Lisp system and so is
function that returns such a Promise. not a pre-reader.
Once rdr has completed a form — in this case, the number 123, There are a few obvious ways the JACL REPL experience could
after about 4000 milliseconds have elapsed — execution continues, be improved. For example, jacl-repl is currently an R[25] script
and 123 is printed to the JavaScript console. requiring an R installation and the chromote[7] package. A stan-
The “read” portion of the JACL REPL is implemented by first in- dalone binary executable is imagined in the future in order to make
stantiating BufferedStream and Reader objects. Then, in an asyn- it easier for developers to start working on JACL projects. Addi-
chronous loop, objects are consumed from the Reader, analyzed, tionally, JACL has yet to define a printer for its native types, or
compiled, and evaluated. an extensible print protocol. Object string representations are ob-
Concurrently, characters may be sent to the BufferedStream tained by calling the generic JavaScript toString() method, which
instantiated by the REPL by calling the write() or writeEach() doesn’t always produce a representation that can be read back in.
methods of the BufferedaStream object. Neither character input
nor read object consumption impede other JavaScript operations, 3.3 Analyzing compiler
so the JACL REPL is suitable for embedding in applications.
Unlike JSCL, the JACL compiler is organized to facilitate high-level
Because of the platform and implementation-dependent nature
optimizations such as those that could support efficient compilation
of the JACL reader, JACL does not support Common Lisp input
of TAGBODY and other fundamental Common Lisp operators.
streams, nor its standard READ and READ-FROM-STRING functions.
The first compiler pass expands macros and produces an AST.
Standard interfaces for extending the reader, such as the
The second compiler pass performs optimizations and produces a
SET-MACRO-CHARACTER function, are not directly supported. How-
new AST. The final pass produces JavaScript code. AST nodes are
ever, the JACL reader does provide an implementation-specific way
represented by generic JavaScript objects with at least the following
to define reader macros.
keys:
• op: The name of the node, as a JavaScript string.
3.2 Chrome DevTools REPL • env: An object of class Env that represents the lexical envi-
A browser-based REPL facilitates experimentation with the lan- ronment of the node.
guage by interested people, from the comfort of their Web browsers. • parent: The parent of the node; this is null for the root.
It’s also a useful debugging feature of a deployed application. • form: The original source data of the node, a Lisp datum.
ELS ’20, April 27–28, 2020, Zürich, Switzerland Alan Dipert

Nodes and Env objects are immutable by convention. Functions JSCL, the existing Lisp closest to JACL, would compile the preceding
are provided for modifying and merging these objects so as only code into approximately3 the following JavaScript:
to produce new objects. This convention reduces the possibility function Jump(id, label) {
of optimization passes interfering with one another. It also eases this.id = id;
understanding the AST, since every AST node contains a copy of this.label = label;
all relevant context. As JavaScript objects, AST nodes are easily }
introspected using the object inspector of the Web browser.
Currently, the Env object tracks evaluation context — one of var X = 10;
statement, expression, or return — lexical variables, and TAGBODY var id = [];
tags. In the future, it will track the remaining aspects of the lexical var label = 0;
environment, such as lexical functions and macros. LOOP: while (true) {
3.3.1 Embedding JavaScript with JACL:%JS. Unlike JSCL or SLip, try {
the JACL compiler supports a special operator for constructing switch(label) {
fragments of JavaScript code, verbatim, from Lisp. The semantics case 0:
of this operator, JACL:%JS, are inspired by a similar feature of if (X === 0) throw new Jump(id, 1);
ClojureScript, js*. For example, the following JACL code displays X = X-1;
the number 3 in an alert box: throw new Jump(id, 0);
case 1:
(JACL:%JS "window.alert(~{})" 3)
default:
The character sequence ~{} is distinct from any plausible JavaScript break LOOP;
syntax and so is used as placeholder syntax. There must be as many }
placeholders as there are arguments to JACL:%JS. } catch (e) {
if (e instanceof Jump && e.id === id) {
3.3.2 Other interoperation support. In addition to JACL:%JS, the
label = e.label;
JACL compiler currently supports three more special operators
} else {
for interacting with the host platform: JACL:%NEW, JACL:%DOT and
throw e;
JACL:%CALL. These operators perform JavaScript object instantia-
}
tion, field access, and function calls, respectively. Since JACL func-
}
tions compile into JavaScript functions, JACL:%CALL is the basis for
}
FUNCALL in JACL, and for function calls generally.
JACL also supplies a convenience macro, JACL:\. or “the dot The mechanism is ingenious. GO tags became switch labels, and
macro” for performing a series of field accesses and method calls2 jumps became throw statements. The thrown objects are instances
concisely. The dot macro takes direct inspiration from the .. macro of Jump. Each instance of Jump contains a destination label.
of Clojure. JACL:\. expands to zero or more nested JACL:%DOT or Unfortunately, in this scheme, every jump requires a JavaScript
JACL:%CALL forms. Here is an example of a JACL:\. form — equiv- exception to be thrown, severely penalizing TAGBODY as previously
alent to the JavaScript expression (123).toString().length — discussed. Fortunately, a straightforward local jump optimization
and its corresponding expansion: can be applied that yields a tremendous performance benefit. Local
(\. 123 (|toString|) |length|) jump optimization is a known technique[28], but JACL is the first
(%DOT (%CALL 123 |toString|) |length|) Lisp targeting JavaScript to apply it.
In order to perform this optimization, the JACL compiler first
Note that JavaScript identifiers are case sensitive, and so case- identifies local GOs in its analysis pass. These are GO nodes with
preserving, pipe-delimited Lisp symbols must be used to refer to no intervening LAMBDA nodes4 between them and their respective,
JavaScript object field and method names. The readtable case of lexically-enclosing TAGBODYs. Then, TAGBODYs are identified that
the JACL reader cannot currently be modified. The dot macro also consist of only local GOs.
recognizes Lisp or JavaScript strings as JavaScript identifiers. JavaScript generated for local GOs does not throw an exception,
3.3.3 TAGBODY compilation strategy. Consider the following Com- but instead leverages the labeled form of the JavaScript continue[20]
mon Lisp program that decrements the local variable X 10 times: statement to transfer control appropriately. JavaScript generated
for TAGBODYs that have been determined to consist only of local
(let ((x 10))
jumps omits the try/catch block, saving on generated code size.
(tagbody
The following code is similar5 to that generated by the JACL
start
compiler. Cursory benchmarks A show JACL code runs several
(when (zerop x) (go end))
(setq x (1- x))
3 Actual JSCL output is not used because it includes type checks, generated variable
(go start)
names, and other code that would obscure the relevant machinery.
end)) 4 Note that LAMBDA doesn’t necessarily preclude local jump optimization if the LAMBDA
is inlined, but JACL currently does not inline functions.
2 Strictly
speaking, JavaScript “method calls” are normal function calls but with a 5 Once more, actual compiler output has been significantly modified and reformatted
particular value of this. for brevity.
JACL: A Common Lisp for Developing Single-Page Web Applications ELS ’20, April 27–28, 2020, Zürich, Switzerland

orders of magnitude faster than JSCL, and that JACL code is almost 6 ACKNOWLEDGMENTS
as fast as the JavaScript statement while(X–): The author wishes to thank Micha Niskin, Bart Botta, Kevin Lynagh,
Lionel Henry, and Andy Keep for invaluable feedback on early
var X = 10; versions of this paper.
var label = 0; The author wishes to express particular thanks to Robert Strandh
LOOP: while (true) { not only for his feedback, but also for his guidance on the writing
switch(label) { process.
case 0: Finally, the author wishes to express special gratitude to his
if (X === 0) { beautiful wife, Sandra Dipert, for her encouragement and support.
label = 1;
continue LOOP; REFERENCES
} [1] Marco Baringer. 2005. Parenscript. Retrieved February 12, 2020 from
X = X - 1; https://web.archive.org/web/20051122141019/http://blogs.bl0rg.net/netzstaub/
label = 0; archives/000525.html
[2] Mihai Bazon. 2012-2018. Implementation notes. Retrieved February 12, 2020 from
continue LOOP; http://lisperator.net/slip/impl
case 1: [3] Mihai Bazon. 2012-2018. SLip — a Lisp system in JavaScript. Retrieved February
default: 12, 2020 from http://lisperator.net/slip/
[4] Mihai Bazon. 2012-2018. Versus Common Lisp. Retrieved February 12, 2020 from
break LOOP; http://lisperator.net/slip/vscl
} [5] Michael Bolin. 2010. Closure: The Definitive Guide: Google Tools to Add Power to
Your JavaScript. O’Reilly Media, Sebastopol, CA, USA.
} [6] Howard I. Cannon. 2007. Flavors: A non-hierarchical approach to object-
oriented programming. Retrieved February 12, 2020 from http://www.
softwarepreservation.org/projects/LISP/MIT/nnnfla1-20040122.pdf
[7] Winston Chang. [n.d.]. chromote: Headless Chrome Web Browser Interface. https:
4 CONCLUSION //github.com/rstudio/chromote
[8] Cognitect, Inc. 2020. Clojure. Retrieved February 12, 2020 from https://clojure.org/
We introduced JACL, a new Common Lisp created to ease SPA [9] Cognitect, Inc. 2020. ClojureScript. Retrieved February 12, 2020 from https:
development. JACL is designed as an efficient, practical tool, with //clojurescript.org/
the needs of industrial SPA developers in mind. JACL integrates [10] Cognitect, Inc. 2020. Companies. Retrieved
https://clojure.org/community/companies from https://clojurescript.org/
tightly with the Web browser platform and interoperates easily community/companies
with JavaScript. Compared to other browser-based Lisps, JACL [11] Evan Czaplicki. 2012. Elm: Concurrent FRP for Functional GUIs. Retrieved
places a higher emphasis on the value of the REPL, and introduces February 12, 2020 from https://elm-lang.org/assets/papers/concurrent-frp.pdf
[12] B. A. Myers et al. 1990. Comprehensive Support for Graphical, Highly-Interactive
new techniques for integrating the REPL into the development User Interfaces: The Garnet User Interface Development Environment. IEEE
workflow. Computer 23, 11 (Nov. 1990), 71–85. https://doi.org/10.1109/2.60882
[13] Google, Inc. 2020. Chrome DevTools. Retrieved February 12, 2020 from https:
//developers.google.com/web/tools/chrome-devtools
[14] Paul Hammant. 2013. Interface Builder’s Alternative Lisp timeline. Retrieved
5 FUTURE WORK February 20, 2020 from https://paulhammant.com/2013/03/28/interface-builders-
alternative-lisp-timeline/
In order to be practical for application development, JACL must [15] Rich Hickey. 2012. ClojureScript Release. Retrieved February 12, 2020 from
https://www.youtube.com/watch?v=tVooR-dF_Ag
support the creation of standalone executables. In the case of JACL, [16] LispWorks Ltd. 2017. deliver. Retrieved February 21, 2020 from http://www.
these would be single JavaScript files that may be included in an lispworks.com/documentation/lw71/DV/html/delivery-220.htm
HTML page and are executed on page load. Fortunately, since JACL [17] Vladimir Sedach Marco Baringer, Henrik Hjelte. 2005-2019. Parenscript Reference
Manual. Retrieved February 12, 2020 from https://common-lisp.net/project/
development is image-based, JACL should support the traditional parenscript/reference.html
approach of specifying a Lisp function entrypoint and dumping the [18] Benedikt Meurer Maya Lekova. 2018. Faster async functions and promises. Re-
Lisp image to native (JavaScript) code. The SAVE-LISP-AND-DIE[26] trieved February 12, 2020 from https://v8.dev/blog/fast-async
[19] Mozilla, Inc. 2020. Concurrency model and the event loop. Retrieved February 22,
function in SBCL and the DELIVER[16] function in LispWorks are 2020 from https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
two examples of this functionality in other implementations. [20] Mozilla, Inc. 2020. label - JavaScript | MDN. Retrieved February 19,
2020 from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
JACL should be able to perform rudimentary optimizations such Statements/label
as global function and variable tree shaking[30] in order to reduce [21] David Nolen. 2020. ClojureScript Quick Start. Retrieved February 12, 2020 from
the size of generated executables. In addition, JACL should make dy- https://clojurescript.org/guides/quick-start
[22] Peter Norvig. 1992. Paradigms of Artificial Intelligence Programming: Case studies
namic function and variable references in executables static, so that in Common Lisp. Morgan Kaufmann Publishers, San Francisco, CA, USA.
third party tools like Google Closure Compiler[5] may optionally [23] David Vázquez Púa. 2018. Growing a Lisp compiler. Retrieved February 12, 2020
be used to perform additional optimization. from https://www.youtube.com/watch?v=XT7JYPtWMd8
[24] David Vázquez Púa and contributors. [n.d.]. jscl-project/jscl. Retrieved February
Other than the ability to produce optimized standalone executa- 12, 2020 from https://github.com/jscl-project/jscl/
bles, many other design and implementation tasks remain, such [25] R Core Team. [n.d.]. R: A Language and Environment for Statistical Computing.
http://www.R-project.org/
as support for special variables in lambda lists, EVAL-WHEN, macro [26] SBCL Project Contributors. 2020. SBCL 2.0.1 User Manual. Retrieved February
lambda lists, DECLARE et al, CLOS, various other data types, com- 21, 2020 from http://www.sbcl.org/manual/
piler macros, etc. The list of tasks is enormous, but it is anticipated [27] Soma Somasegar. 2012. TypeScript: JavaScript Development at Application Scale.
Retrieved February 4, 2020 from https://web.archive.org/web/20121003001910/
that these features can be implemented over time, in the order http://blogs.msdn.com/b/somasegar/archive/2012/10/01/typescript-javascript-
demanded by application development. development-at-application-scale.aspx
ELS ’20, April 27–28, 2020, Zürich, Switzerland Alan Dipert

[28] Robert Strandh. 2020. compile-general-purpose-asts.lisp. Retrieved default:


February 21, 2020 from https://github.com/robert-strandh/SICL/blob/ break LOOP;
2d322d3c7794eb4e89720b9a2fce42395a787376/Code/Cleavir2/AST-to-
HIR/compile-general-purpose-asts.lisp#L200-L303 }
[29] Wikipedia contributors. 2020. Reason (syntax extension for OCaml) — Wikipedia, }
The Free Encyclopedia. https://en.wikipedia.org/w/index.php?title=Reason_
(syntax_extension_for_OCaml)&oldid=940051580 [Online; accessed February 12,
}
2020].
[30] Wikipedia contributors. 2020. Tree shaking — Wikipedia, The Free Encyclope- function baseline_js(X) {
dia. https://en.wikipedia.org/w/index.php?title=Tree_shaking&oldid=908332079
[Online; accessed February 22, 2020]. while(X--);
}
A TAGBODY PERFORMANCE BENCHMARKS
var start = performance.now();
The following benchmark code was run on Google Chrome 80.0.3987.116, for (var i = 0; i < 1e6; i++) tagbody_unoptimized(10);
on Linux, using a computer with an Intel i7-3520M CPU. Times are console.log("tagbody_unoptimized", performance.now() - start);
in milliseconds. // tagbody_unoptimized 58994.43500000052
function Jump(id, label) {
this.id = id; var start = performance.now();
this.label = label; for (var i = 0; i < 1e6; i++) tagbody_optimized(10);
} console.log("tagbody_optimized", performance.now() - start);
// tagbody_optimized 23.61499999812804
function tagbody_unoptimized(X) {
var id = []; var start = performance.now();
var label = 0; for (var i = 0; i < 1e6; i++) baseline_js(10);
LOOP: while (true) { console.log("baseline_js", performance.now() - start);
try { // baseline_js 15.055000003427267
switch(label) {
case 0:
if (X === 0) throw new Jump(id, 1);
X = X - 1;
throw new Jump(id, 0);
case 1:
default:
break LOOP;
}
} catch (e) {
if (e instanceof Jump && e.id === id) {
label = e.label;
} else {
throw e;
}
}
}
}

function tagbody_optimized(X) {
var X = 10;
var label = 0;
LOOP: while (true) {
switch(label) {
case 0:
if (X === 0) {
label = 1;
continue LOOP;
}
X = X - 1;
label = 0;
continue LOOP;
case 1:

You might also like