author | Alan Dipert
<alan@dipert.org> 2020-04-16 09:07:19 UTC |
committer | Alan Dipert
<alan@dipert.org> 2020-04-16 09:07:19 UTC |
parent | b4bd8f443393cb00442c8937cfd6d712484e9e24 |
paper/jacl-demo-els-2020.tex | +232 | -447 |
paper/jacl-els-2020.bib | +8 | -0 |
diff --git a/paper/jacl-demo-els-2020.tex b/paper/jacl-demo-els-2020.tex index af66807..608c7a2 100644 --- a/paper/jacl-demo-els-2020.tex +++ b/paper/jacl-demo-els-2020.tex @@ -26,16 +26,15 @@ \email{alan@dipert.org} \begin{abstract} - This paper demonstrates JavaScript-Assisted Common Lisp (JACL), a - new Web-browser based implementation of an extended subset of Common - Lisp. While still in the early stages of development, JACL is an - effort to facilitate the eventual use of Common Lisp in overcoming - the challenges of Single-page Web Application (SPA) - development. JACL promotes interactive development in the Web - browser environment with its \emph{asynchronous reader} and Chrome - DevTools-based REPL client. JACL includes an optimizing - Lisp-to-JavaScript compiler and supports interoperation with - JavaScript. + This paper demonstrates JavaScript-Assisted Common Lisp (JACL), an + experimental Web-browser based implementation of an extended subset + of Common Lisp. JACL, which is in the early stages of development, + is an effort to explore new techniques for large-scale Single-page + Web Application (SPA) development in Lisp. JACL includes an + optimizing Lisp-to-JavaScript compiler and interoperates with + JavaScript. JACL promotes interactive, \emph{residential} + development in the Web browser environment with its + \emph{asynchronous reader} and Chrome DevTools-based REPL client. \end{abstract} \begin{CCSXML} @@ -60,7 +59,7 @@ \maketitle -\section{Motivation} +\section{Introduction} The demand for SPAs in the past decade has only grown, and users and stakeholders continually expect larger and more sophisticated @@ -75,442 +74,228 @@ development workflows, and claims some advantage relative to the status quo. This paper demonstrates one new such language, JavaScript-Assisted -Common Lisp (JACL), an implementation of an extended subset of Common -Lisp. The primary goal of the JACL project is to ease SPA development -by applying Common Lisp --- a proven\cite{Cannon07,Garnet90,Action} -substrate for UI innovation --- to the difficult challenges now faced -by developers. The JACL language is envisioned as the means to -achieving that goal. - -\section{Related Work} - -Many similar efforts to compile Lisp to JavaScript precede JACL. Lisps -that have either demonstrated industrial utility or that implement a -significant subset of Common Lisp are surveyed in appendix -\ref{appendix:lisps}. Compared to related work, JACL distinguishes -itself primarily by its emphasis on interactive development and by its -\emph{asynchronous reader} facility. - -\section{Interactive Development Model} - -\section{JavaScript FFI} - -\section{Object Inspection} - -%\section{Design and Implementation} -% -%From a design perspective, JACL is an effort to balance the -%requirements of an interactive and practical Lisp development -%environment with the constraints imposed by the Web browser -%platform. JACL proposes several innovations with respect to previous -%work in pursuit of this balance. -% -%\subsection{Asynchronous reader} -% -%The basis for interactive development in Lisp is undeniably the REPL, -%but as the JSCL ``pre-reader'' demonstrates, even the direct approach -%to this simple mechanism is hampered by the asynchronous model of -%input imposed by JavaScript.\cite{EventLoop}. Traditionally, Lisp -%readers are implemented in environments with a blocking function for -%obtaining input, like \texttt{getc(1)} on Unix. The blocking nature of -%input consumption allows the reader to consume nested input -%recursively, using the call stack to accumulate structures. In -%JavaScript, input arrives asynchronously, and only when the call stack -%is empty. To mitigate this difficulty, the JACL reader facility is -%completely asynchronous. Conceptually, it is the JSCL REPL -%``pre-reader'' taken to its inevitable conclusion. -% -%\section{Design and Implementation} -% -%From a design perspective, JACL is an effort to balance the -%requirements of an interactive and practical Lisp development -%environment with the constraints imposed by the Web browser -%platform. JACL proposes several innovations with respect to previous -%work in pursuit of this balance. -% -%\subsection{Asynchronous reader} -% -%The basis for interactive development in Lisp is undeniably the REPL, -%but as the JSCL ``pre-reader'' demonstrates, even the direct approach -%to this simple mechanism is hampered by the asynchronous model of -%input imposed by JavaScript.\cite{EventLoop}. Traditionally, Lisp -%readers are implemented in environments with a blocking function for -%obtaining input, like \texttt{getc(1)} on Unix. The blocking nature of -%input consumption allows the reader to consume nested input -%recursively, using the call stack to accumulate structures. In -%JavaScript, input arrives asynchronously, and only when the call stack -%is empty. To mitigate this difficulty, the JACL reader facility is -%completely asynchronous. Conceptually, it is the JSCL REPL -%``pre-reader'' taken to its inevitable conclusion. -% -%The JACL reader is implemented as a JavaScript class, -%\texttt{Reader}. \texttt{Reader} instances are parameterized by an -%input source. One such input source is the \texttt{BufferedStream} -%class. The input source asynchronously notifies the reader instance -%when characters are available. The reader incrementally consumes these -%characters. Once the reader has accumulated a Lisp datum, it notifies -%its subscribers of the availability of the datum. -% -%The JACL reader implementation makes extensive use of modern -%JavaScript features to support asynchronous programming including -%promises, iterators, async functions, async iterators, and the -%\texttt{await} keyword. These features simplify the JACL -%implementation and aid its performance \cite{V8async}. It is hoped -%that JACL will eventually be written in itself, and that these -%features will be accessible from Lisp, perhaps as a set of -%implementation-dependent \emph{declaration specifiers} available in -%\texttt{DECLARE} expressions. -% -%The following example demonstrates, in JavaScript, the process by -%which the JACL reader consumes characters and produces Lisp objects. A -%\texttt{BufferedStream} and \texttt{Reader} are instantiated, sent -%characters asynchronously, and then the resulting Lisp object is -%printed to the JavaScript console. -% -%\begin{verbatim} -%(async () => { -% let bs = new BufferedStream(), -% rdr = new Reader(bs); -% -% window.setTimeout(() => bs.write("1"), 1000); -% window.setTimeout(() => bs.write("2"), 2000); -% window.setTimeout(() => bs.write("3"), 3000); -% window.setTimeout(() => bs.write(" "), 4000); -% -% console.log(await rdr.read()); -%})(); -%\end{verbatim} -% -%\noindent In the preceding example, \texttt{window.setTimeout()} is -%used to enqueue several JavaScript functions for execution after 1000, -%2000, 3000, and 4000 milliseconds. Each enqueued function writes a -%character of input to the \texttt{BufferedStream} \texttt{bs} when -%invoked. -% -%Before any enqueued function is invoked, execution proceeds to the -%\texttt{console.log} call, but is suspended by the \texttt{await} -%keyword. -% -%The \texttt{await} keyword expects a JavaScript \texttt{Promise} -%object on its right side, and JavaScript execution remains suspended -%until the \texttt{Promise} has ``resolved'', or notified its -%subscribers that the pending computation it represents has -%completed. \texttt{rdr.read()} is an \texttt{async} function that -%returns such a \texttt{Promise}. -% -%Once \texttt{rdr} has completed a form --- in this case, the number -%123, after about 4000 milliseconds have elapsed --- execution -%continues, and \texttt{123} is printed to the JavaScript console. -% -%The ``read'' portion of the JACL REPL is implemented by first -%instantiating \texttt{BufferedStream} and \texttt{Reader} -%objects. Then, in an asynchronous loop, objects are consumed from the -%\texttt{Reader}, analyzed, compiled, and evaluated. -% -%Concurrently, characters may be sent to the \texttt{BufferedStream} -%instantiated by the REPL by calling the \texttt{write()} or -%\texttt{writeEach()} methods of the \texttt{BufferedaStream} -%object. Neither character input nor read object consumption impede -%other JavaScript operations, so the JACL REPL is suitable for -%embedding in applications. -% -%Because of the platform and implementation-dependent nature of the -%JACL reader, JACL does not support Common Lisp input streams, nor its -%standard \texttt{READ} and \texttt{READ-FROM-STRING} -%functions. Standard interfaces for extending the reader, such as the -%\linebreak\texttt{SET-MACRO-CHARACTER} function, are not directly -%supported. However, the JACL reader does provide an -%implementation-specific way to define reader macros. -% -%\subsection{Chrome DevTools REPL} -% -%A browser-based REPL facilitates experimentation with the language by -%interested people, from the comfort of their Web browsers. It's also a -%useful debugging feature of a deployed application. -% -%However, most developers already have a preferred text editor and a -%REPL interaction workflow, and so it's not within the JACL project -%scope to build a resident IDE in the style of SLip. Even if a resident -%IDE was a goal, the file system access restrictions imposed by the -%browser would present significant challenges. -% -%Instead, JACL offers an alternative development REPL approach that -%requires minimal host tooling: the DevTools-based REPL. Google Chrome -%is capable of hosting a WebSocket-based debug server that implements -%the DevTools Protocol \cite{GDevTools}. DevTools Protocol clients may -%then connect to the server and interact with open tabs, such as by -%evaluating arbitrary JavaScript within the context of the tab. JACL -%leverages the DevTools Protocol to deliver a command-line REPL client -%that may be run on development machines. The workflow is the -%following: -% -%\begin{enumerate} -% \item Run Google Chrome from the shell with the \linebreak -% \texttt{--remote-debugging-port} parameter. -% \item Navigate to the Web site hosting the JACL system you wish to -% interact with. -% \item Run \texttt{jacl-repl} from the shell. -% \item Be presented with a Lisp prompt. -%\end{enumerate} -% -%As a simple command-line application with a textual interface, -%\texttt{jacl-repl} can be run in various contexts. For example, it -%could be run within an Emacs ``inferior-lisp'' buffer, and then Lisp -%forms could be sent from other Emacs buffers for evaluation in the -%REPL. It could also be run as part of a build process that pipes Lisp -%sources over the WebSocket for batch compilation. It is anticipated -%that additional host-side tools that depend on \texttt{jacl-client} -%will be necessary in the future to support loading source files in -%dependency order. -% -%Unlike the pre-readers of SLip and JSCL, \texttt{jacl-client} is -%completely ignorant of Lisp syntax. \texttt{jacl-client} merely -%transports characters between the host machine and the Lisp system and -%so is \emph{not} a pre-reader. -% -%There are a few obvious ways the JACL REPL experience could be -%improved. For example, \texttt{jacl-repl} is currently an -%R\cite{Rstats} script requiring an R installation and the -%\texttt{chromote}\cite{Rchromote} package. A standalone binary -%executable is imagined in the future in order to make it easier for -%developers to start working on JACL projects. Additionally, JACL has -%yet to define a printer for its native types, or an extensible print -%protocol. Object string representations are obtained by calling the -%generic JavaScript \texttt{toString()} method, which doesn't always -%produce a representation that can be read back in. -% -%\subsection{Analyzing compiler} -% -%Unlike JSCL, the JACL compiler is organized to facilitate high-level -%optimizations such as those that could support efficient compilation -%of \texttt{TAGBODY} and other fundamental Common Lisp operators. -% -%The first compiler pass expands macros and produces an AST. The second -%compiler pass performs optimizations and produces a new AST. The final -%pass produces JavaScript code. AST nodes are represented by generic -%JavaScript objects with at least the following keys: -% -%\begin{itemize} -% \item \texttt{op}: The name of the node, as a JavaScript string. -% \item \texttt{env}: An object of class \texttt{Env} that represents -% the lexical environment of the node. -% \item \texttt{parent}: The parent of the node; this is \texttt{null} -% for the root. -% \item \texttt{form}: The original source data of the node, a Lisp -% datum. -%\end{itemize} -% -%\noindent Nodes and \texttt{Env} objects are immutable by -%convention. Functions are provided for modifying and merging these -%objects so as only to produce new objects. This convention reduces the -%possibility of optimization passes interfering with one another. It -%also eases understanding the AST, since every AST node contains a copy -%of all relevant context. As JavaScript objects, AST nodes are easily -%introspected using the object inspector of the Web browser. -% -%Currently, the \texttt{Env} object tracks evaluation context --- one -%of \emph{statement}, \emph{expression}, or \emph{return} --- lexical -%variables, and \texttt{TAGBODY} tags. In the future, it will track the -%remaining aspects of the lexical environment, such as lexical -%functions and macros. -% -%\subsubsection{Embedding JavaScript with \texttt{JACL:\%JS}} -% -%Unlike JSCL or SLip, the JACL compiler supports a special operator for -%constructing fragments of JavaScript code, verbatim, from Lisp. The -%semantics of this operator, \texttt{JACL:\%JS}, are inspired by a -%similar feature of ClojureScript, \texttt{js*}. For example, the -%following JACL code displays the number 3 in an alert box: -% -%\begin{verbatim} -%(JACL:%JS "window.alert(~{})" 3) -%\end{verbatim} -% -%The character sequence \texttt{\textasciitilde\{\}} is distinct from -%any plausible \linebreak JavaScript syntax and so is used as placeholder -%syntax. There must be as many placeholders as there are arguments to -%\texttt{JACL:\%JS}. -% -%\subsubsection{Other interoperation support} -% -%In addition to \texttt{JACL:\%JS}, the JACL compiler currently -%supports three more special operators for interacting with the host -%platform: \texttt{JACL:\%NEW}, \texttt{JACL:\%DOT} and -%\texttt{JACL:\%CALL}. These operators perform JavaScript object -%instantiation, field access, and function calls, respectively. Since -%JACL functions compile into JavaScript functions, \texttt{JACL:\%CALL} -%is the basis for \texttt{FUNCALL} in JACL, and for function calls -%generally. -% -%JACL also supplies a convenience macro, \texttt{JACL:\textbackslash.} -%or ``the dot macro'' for performing a series of field accesses and -%method calls\footnote{Strictly speaking, JavaScript ``method calls'' -% are normal function calls but with a particular value of -% \texttt{this}.} concisely. The dot macro takes direct inspiration -%from the \texttt{..} macro of Clojure. \texttt{JACL:\textbackslash.} -%expands to zero or more nested \texttt{JACL:\%DOT} or -%\texttt{JACL:\%CALL} forms. Here is an example of a -%\texttt{JACL:\textbackslash.} form --- equivalent to the JavaScript -%expression \texttt{(123).toString().length} --- and its corresponding -%expansion: -% -%\begin{verbatim} -%(\. 123 (|toString|) |length|) -%(%DOT (%CALL 123 |toString|) |length|) -%\end{verbatim} -% -%\noindent Note that JavaScript identifiers are case sensitive, and so -%case-preserving, pipe-delimited Lisp symbols must be used to refer to -%JavaScript object field and method names. The \emph{readtable case} of -%the JACL reader cannot currently be modified. The dot macro also -%recognizes Lisp or JavaScript strings as JavaScript identifiers. -% -%\subsubsection{\texttt{TAGBODY} compilation strategy} -% -%Consider the following Common Lisp program that decrements the local -%variable \texttt{X} 10 times: -% -%\begin{verbatim} -%(let ((x 10)) -% (tagbody -% start -% (when (zerop x) (go end)) -% (setq x (1- x)) -% (go start) -% end)) -%\end{verbatim} -% -%\noindent JSCL, the existing Lisp closest to JACL, would compile the -%preceding code into approximately\footnote{Actual JSCL output is not -% used because it includes type checks, generated variable names, and -% other code that would obscure the relevant machinery.} the following -%JavaScript: -% -%\begin{verbatim} -%function Jump(id, label) { -% this.id = id; -% this.label = label; -%} -% -%var X = 10; -%var id = []; -%var label = 0; -%LOOP: while (true) { -% try { -% 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; -% } -% } -%} -%\end{verbatim} -% -%\noindent The mechanism is ingenious. \texttt{GO} tags became -%\texttt{switch} labels, and jumps became \texttt{throw} -%statements. The thrown objects are instances of \texttt{Jump}. Each -%instance of \texttt{Jump} contains a destination label. -% -%Unfortunately, in this scheme, every jump requires a JavaScript -%exception to be thrown, severely penalizing \texttt{TAGBODY} as -%previously discussed. Fortunately, a straightforward \emph{local jump -% optimization} can be applied that yields a tremendous performance -%benefit. Local jump optimization is a known -%technique\cite{SICLTagbody}, but JACL is the first Lisp targeting -%JavaScript to apply it. -% -%In order to perform this optimization, the JACL compiler first -%identifies local \texttt{GO}s in its analysis pass. These are -%\texttt{GO} nodes with no intervening \texttt{LAMBDA} -%nodes\footnote{Note that \texttt{LAMBDA} doesn't necessarily preclude -% local jump optimization if the \texttt{LAMBDA} is inlined, but JACL -% currently does not inline functions.} between them and their -%respective, lexically-enclosing \texttt{TAGBODY}s. Then, -%\texttt{TAGBODY}s are identified that consist of only local -%\texttt{GO}s. -% -%JavaScript generated for local \texttt{GO}s does not throw an -%exception, but instead leverages the labeled form of the JavaScript -%\texttt{continue}\cite{MozLabel} statement to transfer control -%appropriately. JavaScript generated for \texttt{TAGBODY}s that have -%been determined to consist only of local jumps omits the -%\texttt{try/catch} block, saving on generated code size. -% -%The following code is similar\footnote{Once more, actual compiler -% output has been significantly modified and reformatted for brevity.} -%to that generated by the JACL compiler. Cursory benchmarks -%\ref{appendix:benchmarks} show JACL code runs several orders of -%magnitude faster than JSCL, and that JACL code is almost as fast as -%the JavaScript statement \texttt{while(X--)}: -% -%\begin{verbatim} -%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: -% default: -% break LOOP; -% } -%} -%\end{verbatim} -% -%\section{Conclusion} -% -%We introduced JACL, a new Common Lisp created to ease SPA development. -%JACL is designed as an efficient, practical tool, with the needs of -%industrial SPA developers in mind. JACL integrates tightly with the -%Web browser platform and interoperates easily with -%JavaScript. Compared to other browser-based Lisps, JACL places a -%higher emphasis on the value of the REPL, and introduces new -%techniques for integrating the REPL into the development workflow. -% -%\section{Future Work} -% -%In order to be practical for application development, JACL must -%support the creation of standalone executables. In the case of JACL, -%these would be single JavaScript files that may be included in an HTML -%page and are executed on page load. Fortunately, since JACL -%development is image-based, JACL should support the traditional -%approach of specifying a Lisp function entrypoint and dumping the Lisp -%image to native (JavaScript) code. The -%\texttt{SAVE-LISP-AND-DIE}\cite{SBCLManual} function in SBCL and the -%\texttt{DELIVER}\cite{LispWorksDeliver} function in LispWorks are two -%examples of this functionality in other implementations. -% -%JACL should be able to perform rudimentary optimizations such as -%global function and variable tree shaking\cite{wiki:TreeShaking} in -%order to reduce the size of generated executables. In addition, JACL -%should make dynamic function and variable references in executables -%static, so that third party tools like Google Closure -%Compiler\cite{Bolin10} may optionally be used to perform additional -%optimization. -% -%Other than the ability to produce optimized standalone executables, -%many other design and implementation tasks remain, such as support for -%special variables in lambda lists, \texttt{EVAL-WHEN}, macro lambda -%lists, \texttt{DECLARE} et al, CLOS, various other data types, -%compiler macros, etc. The list of tasks is enormous, but it is -%anticipated that these features can be implemented over time, in the -%order demanded by application development. +Common Lisp (JACL), an experimental implementation of an extended +subset of Common Lisp. JACL was created to explore new techniques for +applying Common Lisp --- a proven\cite{Cannon07,Garnet90,Action} +substrate for UI innovation --- to SPA development. + +Many projects involving compilation of Lisp to JavaScript precede +JACL. Lisps that have either demonstrated industrial utility or that +implement a significant subset of Common Lisp are surveyed in appendix +\ref{appendix:lisps}. Like many of these related efforts, JACL +includes an online, optimizing compiler and supports interoperation +with JavaScript. These Lisps, and most other SPA implementation +languages, are delivered as tools primarily designed to be run in the +host environment, instead of in the target Web browser environment +where compiled applications ultimately do. JACL distinguishes itself +from these efforts by placing special emphasis on the value of +\emph{residential} development style, where both applications and the +tools used to create them co-evolve in a shared environment. JACL +provides fundamental support for residential development with its +\emph{asynchronous reader}. + +\section{Interoperation with JavaScript} + +JACL integrates tightly with JavaScript and depends heavily on the +JavaScript runtime. As a result, JACL enjoys roughly the same +applicability and performance characteristics as the JavaScript +platform. However, this high degree of integration is at odds with +comformance to the Common Lisp specification, and so JACL will never +strictly conform. + +\subsection{Object Types} + +JACL introduces several of its own object types, currently implemented +in JavaScript, including \texttt{Cons}, \texttt{LispSymbol}, and +\texttt{LispString}. \texttt{Cons} and \texttt{LispSymbol} are +introduced because JavaScript does not include direct +equivalents. \texttt{LispString} is introduced because the native +JavaScript \texttt{String} is immutable, whereas Lisp strings are +mutable. + +JACL includes support for only one numeric type, the JavaScript +\texttt{Number} object. The JavaScript \texttt{Number} is a +double-precision 64-bit IEEE 754 value. The JACL reader interprets +integers as \texttt{Number} objects. In the future, JACL will also +interpret floating-point numbers as \texttt{Number}. This decision +trades ANSI conformance for performance. If either type were boxed, +arithmetic performance would suffer intolerably. JSCL\cite{JSCLGitHub} +and Valtan\cite{valtanGitHub} make the same tradeoff. + +JACL functions are JavaScript functions, and may be invoked by +JavaScript callbacks without a special calling convention. JavaScript +functions named as Lisp values may be invoked with \texttt{FUNCALL} or +\texttt{APPLY}. As opposed to JSCL, neither arguments nor return +values are automatically coerced to or from any particular set of +object types. + +\subsection{Operators} + +The JACL compiler supports a special operator for constructing +fragments of JavaScript code, verbatim, from Lisp. The semantics of +this operator, \texttt{JACL:\%JS}, are inspired by a similar feature +of ClojureScript\cite{Cljs}, \texttt{js*}. For example, the following +JACL code displays the number 3 in an alert box: + +\begin{verbatim} +(JACL:%JS "window.alert(~{})" 3) +\end{verbatim} + +The character sequence \texttt{\textasciitilde\{\}} is distinct from +any plausible \linebreak JavaScript syntax and so is used as +placeholder syntax. There must be as many placeholders as there are +arguments to \texttt{JACL:\%JS}. + +In addition to \texttt{JACL:\%JS}, the JACL compiler currently +supports three more special operators for interacting with the host +platform: \texttt{JACL:\%NEW}, \texttt{JACL:\%DOT} and +\texttt{JACL:\%CALL}. These operators perform JavaScript object +instantiation, field access, and function calls, respectively. Since +JACL functions are JavaScript functions, \texttt{JACL:\%CALL} is the +basis for \texttt{FUNCALL} in JACL, and for function calls generally. + +JACL also supplies a convenience macro, \texttt{JACL:\textbackslash.} +or ``the dot macro'' for performing a series of field accesses and +method calls\footnote{Strictly speaking, JavaScript ``method calls'' + are normal function calls but with a particular value of + \texttt{this}.} concisely. The dot macro takes direct inspiration +from the \texttt{..} macro of +Clojure\cite{Clojure}. \texttt{JACL:\textbackslash.} expands to zero +or more nested \texttt{JACL:\%DOT} or \texttt{JACL:\%CALL} forms. Here +is an example of a \texttt{JACL:\textbackslash.} form --- equivalent +to the JavaScript expression \texttt{(123).toString().length} --- and +its corresponding expansion: + +\begin{verbatim} +(\. 123 (|toString|) |length|) +(%DOT (%CALL 123 |toString|) |length|) +\end{verbatim} + +\noindent Note that JavaScript identifiers are case sensitive, and so +case-preserving, pipe-delimited Lisp symbols must be used to refer to +JavaScript object field and method names. The \emph{readtable case} of +the JACL reader cannot currently be modified. The dot macro also +recognizes Lisp or JavaScript strings as JavaScript identifiers. + +\subsection{Reader Macros} + +JACL includes two reader macros to support interoperation with +JavaScript. These macros may be added to the \texttt{*READTABLE*} by +calling the function +\texttt{(JACL:ENABLE-JS-SYNTAX)}. \texttt{@\char`\"} denotes +JavaScript \texttt{String} objects and \texttt{@|} denotes JavaScript +identifiers. + +For example, the following two forms, which both evaluate to a JavaScript +\texttt{String}, are equivalent: + +\begin{verbatim} +@"Hello" +(\. "Hello" (|toString|)) +\end{verbatim} + +\noindent \texttt{@|} may generally be used in place of the +\texttt{JACL:\%JS} special form to refer to JavaScript +identifiers. \texttt{(JACL:\%JS \char`\"alert\char`\")} and +\texttt{@|alert|} are equivalent. + +\section{Running JACL Programs} + +Currently, JACL programs may be evaluated in the Web browser in two +ways: by adding Lisp \texttt{<script>} tags to the \texttt{<head>} of +a Web page that also includes \texttt{jacl.js}, or by using the +\texttt{jacl} tool included in the JACL +distribution\cite{JACLDistribution} to connect to a running Web +browser. + +\subsection{Lisp Scripts} + +Development of JACL itself is currently driven primarily by modifying +\texttt{jacl.js} and the \texttt{boot.lisp} and +\texttt{jacl-tests.lisp} Lisp scripts. The Lisp test scripts are +included in the \texttt{index.html} file in the JACL +distribution\cite{JACLDistribution}. After each modification, the Web +browser is reloaded, and test results are displayed. + +This workflow is characteristic of the traditional JavaScript +development workflow and has served JACL development so far. However, +it requires runtime parsing and compilation of JACL source code, among +other inefficiencies. Reloading the Web browser also destroys the +entire runtime environment. + +The easiest way to create JACL programs in this manner is to start +with the \texttt{index.html} Web page provided by JACL and then modify +it by removing or adding new Lisp scripts. + +It is imagined that ultimately, Lisp sources will be incorporated into +the Lisp \emph{image} exclusively by the REPL client tool. An +arrangement such as this decouples source code loading from the Web +browser lifecycle. Production executables may then be produced at any +time from the Lisp image by a Lisp function in a manner similar to the +\texttt{SAVE-LISP-AND-DIE}\cite{SBCLManual} function in SBCL or the +\texttt{DELIVER}\cite{LispWorksDeliver} function in LispWorks. + +\subsection{REPL} + +JACL includes a REPL client program, \texttt{jacl}, that may be used +to execute JACL programs in a Web browser from a terminal on the +host. This process is described in detail in the \texttt{RUN.md} +document included in the JACL distribution\cite{JACLDistribution}, but +is summarized here. + +In order to use the REPL, the user must first start either the Google +Chrome or Chromium browser with the remote debugging feature enabled. +With remote debugging enabled, the Web browser may be controlled using +a client program over a WebSocket connection. Then, the user must +navigate to a Web page that includes at least \texttt{jacl.js} and +\texttt{boot.lisp}. + +Finally, the user must start the \texttt{jacl} REPL client in a +terminal. \texttt{jacl} leverages the remote debugging feature as a +REPL transport, using it to send and receive characters between the +host and the remote JACL runtime. The \texttt{jacl} tool is currently +written in R\cite{Rstats} and uses the +\texttt{chromote}\cite{Rchromote} package for interacting with the +remote Chrome or Chromium browser. + +The \texttt{jacl} program has no knowledge of JACL syntax or +semantics; it merely sends and receives characters. The intentional +simplicity of \texttt{jacl} is part of the larger project goal of +promoting residential-style tool and program development in the target +environment. The simplicity of \texttt{jacl} is possible because of +the asynchronous nature of the JACL reader. Incoming characters +delivered over the WebSocket debugging connection are received by +callback functions in the Web browser. The received characters are +asynchronously and incrementally parsed into Lisp data. When a +complete datum is formed, the compiler is called, and the resulting +JavaScript is evaluated. Finally, any output is sent back over the +debugger connection and received and printed by the \texttt{jacl} +program. + +\section{Conclusion} + +We introduced JACL, a new and experimental Common Lisp created to +explore techniques for building sophisticated SPAs. JACL integrates +tightly with the Web browser platform and interoperates directly with +JavaScript. Compared to other browser-based Lisps, JACL promotes +residential development, and introduces a new technique for +integrating the REPL into the development workflow. + +\section{Future Work} + +JACL currently lacks many basic Common Lisp data types, functions, and +operators, and the resulting programming experience is challenging and +wholly impractical. JACL should support as much of Common Lisp as is +possible, accounting for the brutal limitations imposed by JavaScript +and the Web platform. Fortunately, the many other existing Common +Lisps that compile to JavaScript demonstrate that a compelling +implementation \emph{is} possible. + +Other than work related to missing features such as multiple values, +CLOS, and the conditions system, much design work remains with regard +to the specific affordances of the \texttt{jacl} tool. For example, +it's unclear how a large JACL project involving library dependencies +and multiple source files should be managed and loaded. \section{Acknowledgments} diff --git a/paper/jacl-els-2020.bib b/paper/jacl-els-2020.bib index 477b365..fd8f0f5 100644 --- a/paper/jacl-els-2020.bib +++ b/paper/jacl-els-2020.bib @@ -158,6 +158,14 @@ lastaccessed="February 12, 2020", } +@online{JACLDistribution, + author={{Alan Dipert}}, + year={2020}, + title="JACL", + url="https://tailrecursion.com/JACL/", + lastaccessed="April 16, 2020", +} + @misc{CljsRelease, author= {Rich Hickey}, year= {2012},