source: trunk/abcl/doc/manual/abcl.tex @ 13625

Last change on this file since 13625 was 13625, checked in by ehuelsmann, 10 years ago

More documentation of the Java FFI.

File size: 24.5 KB
Line 
1% -*- mode: latex; -*-
2% http://en.wikibooks.org/wiki/LaTeX/
3\documentclass[10pt]{book}
4\usepackage{abcl}
5
6\begin{document}
7\title{A Manual for Armed Bear Common Lisp}
8\date{October 2, 2011}
9\author{Mark~Evenson, Erik~Huelsmann, Alessio~Stalla, Ville~Voutilainen}
10
11\maketitle
12
13\chapter{Introduction}
14
15Armed Bear is a mostly conforming implementation of the ANSI Common
16Lisp standard.  This manual documents the Armed Bear Common Lisp
17implementation for users of the system.
18
19\subsection{Version}
20This manual corresponds to abcl-0.28.0, as yet unreleased.
21
22\chapter{Running}
23
24ABCL is packaged as a single jar file usually named either
25``abcl.jar'' or something like``abcl-0.28.0.jar'' if you are using a
26versioned package from your system vendor.  This byte archive can be
27executed under the control of a suitable JVM by using the ``-jar''
28option to parse the manifest, and select the named class
29(org.armedbear.lisp.Main) for excution:
30
31\begin{listing-shell}
32  cmd$ java -jar abcl.jar
33\end{listing-shell}
34
35N.b. for the proceeding command to work, the ``java'' executable needs
36to be in your path.
37
38To make it easier to facilitate the use of ABCL in tool chains (such as
39SLIME) the invocation is wrapped in a Bourne shell script under UNIX
40or a DOS command script under Windows so that ABCL may be executed
41simply as:
42
43\begin{listing-shell}
44  cmd$ abcl
45\end{listing-shell}
46
47\section{Options}
48
49ABCL supports the following options:
50
51\begin{verbatim}
52--help
53    Displays this message.
54--noinform
55    Suppresses the printing of startup information and banner.
56--noinit
57    Suppresses the loading of the '~/.abclrc' startup file.
58--nosystem
59    Suppresses loading the 'system.lisp' customization file.
60--eval <FORM>
61    Evaluates the <FORM> before initializing REPL.
62--load <FILE>
63    Loads the file <FILE> before initializing REPL.
64--load-system-file <FILE>
65    Loads the system file <FILE> before initializing REPL.
66--batch
67    The process evaluates forms specified by arguments and possibly by those
68    by those in the intialization file '~/.abcl', and then exits.
69
70The occurance of '--' copies the remaining arguments, unprocessed, into
71the variable EXTENSIONS:*COMMAND-LINE-ARGUMENT-LIST*.
72\end{verbatim}
73
74All of the command line arguments which follow the occurrence of ``--''
75are passed into a list bound to the EXT:*COMMAND-LINE-ARGUMENT-LIST*
76variable.
77
78\section{Initialization}
79
80If the ABCL process is started without the ``--noinit'' flag, it
81attempts to load a file named ``.abclrc'' located in the user's home
82directory and then interpret its contents. 
83
84The user's home directory is determined by the value of the JVM system
85property ``user.home''.
86
87\chapter{Conformance}
88
89\section{ANSI Common Lisp}
90ABCL is currently a non-conforming ANSI Common Lisp implementation due
91to the following issues:
92
93\begin{itemize}
94  \item Missing statement of conformance in accompanying documentation
95  \item The generic function signatures of the DOCUMENTATION symbol do
96    not match the CLHS.
97\end{itemize}
98
99ABCL aims to be be a fully conforming ANSI Common Lisp
100implementation.  Any other behavior should be reported as a bug.
101
102\section{Contemporary Common Lisp}
103In addition to ANSI conformance, ABCL strives to implement features
104expected of a contemporary Common Lisp.
105\begin{itemize}
106  \item Incomplete (A)MOP
107    % N.B.
108    % TODO go through AMOP with symbols, starting by looking for
109    % matching function signature.
110    % XXX is this really blocking ANSI conformance?  Answer: we have
111    % to start with such a ``census'' to determine what we have.
112  \item Incomplete Streams:  need suitable abstraction between ANSI
113    and Gray streams.
114   
115\end{itemize}
116
117\chapter{Interaction with host JVM}
118
119% describe calling Java from Lisp, and calling Lisp from Java,
120% probably in two separate sections.  Presumably, we can partition our
121% audience into those who are more comfortable with Java, and those
122% that are more comforable with Lisp
123
124\section{Lisp to Java}
125
126ABCL offers a number of mechanisms to interact with Java from
127its lisp environment. It allows calling methods (and static methods) of
128Java objects, manipulation of fields and static fields and construction
129of new Java objects.
130
131When calling Java routines, some values will automatically be converted
132by the FFI from Lisp values to Java values. These conversions typically
133apply to strings, integers and floats. Other values need to be converted
134to their Java equivalents by the programmer before calling the Java
135object method. Java values returned to Lisp are also generally converted
136back to their Lisp counterparts. Some operators make an exception to this
137rule and do not perform any conversion; those are the ``raw'' counterparts
138of certain FFI functions and are recognizable by their name ending with
139\code{-RAW}.
140
141\subsection{Lowlevel Java API}
142
143There's a higher level Java API defined in the
144\ref{topic:Higher level Java API: JSS}(JSS package) which is available
145in the contrib/ directory. This package is described later in this
146document.  This section covers the lower level API directly available
147after evaluating \code{(require 'JAVA)}.
148
149\subsubsection{Calling Java object methods}
150
151There are two ways to call a Java object method in the basic API:
152
153\begin{itemize}
154\item Call a specific method reference (pre-acquired)
155\item Dynamic dispatch using the method name and
156  the call-specific arguments provided by finding the
157  \ref{section:Parameter matching for FFI dynamic dispatch}{best match}.
158\end{itemize}
159
160The dynamic dispatch variant is discussed in the next section.
161
162\code{JAVA:JMETHOD} is used to acquire a specific method reference.
163The function takes at two or more arguments. The first is Java class designator
164(a \code{JAVA:JAVA-CLASS} object returned by \code{JAVA:JCLASS} or a string naming
165a Java class). The second is a string naming the method.
166
167Any arguments beyond the first two should be strings naming Java classes with
168one exception as listed in the next paragraph. These
169classes specify the types of the arguments for the method to be returned.
170
171There's additional calling convention to the \code{JAVA:JMETHOD} function:
172When the method is called with three parameters and the last parameter is an
173integer, the first method by that name and matching number of parameters is
174returned.
175
176Once you have a reference to the method, you can call it using \code{JAVA:JCALL},
177which takes the method as the first argument. The second argument is the
178object instance to call the method on, or \code{NIL} in case of a static method.
179Any remaining parameters are used as the remaining arguments for the call.
180
181\subsubsection{Calling Java object methods: dynamic dispatch}
182
183The second way of calling Java object methods is by using dynamic dispatch.
184In this case \code{JAVA:JCALL} is used directly without acquiring a method
185reference first. In this case, the first argument provided to \code{JAVA:JCALL}
186is a string naming the method to be called. The second argument is the instance
187on which the method should be called and any further arguments are used to
188select the best matching method and dispatch the call.
189
190\subsubsection{Dynamic dispatch: caveats}
191
192Dynamic dispatch is performed by using the Java reflection API. Generally
193it works fine, but there are corner cases where the API does not correctly
194reflect all the details involved in calling a Java method. An example is
195the following Java code:
196
197\begin{listing-java}
198ZipFile jar = new ZipFile("/path/to/some.jar");
199Object els = jar.entries();
200Method method = els.getClass().getMethod("hasMoreElements");
201method.invoke(els);
202\end{listing-java}
203
204even though the method \code{hasMoreElements} is public in \code{Enumeration},
205the above code fails with
206
207\begin{listing-java}
208java.lang.IllegalAccessException: Class ... can
209not access a member of class java.util.zip.ZipFile$2 with modifiers
210"public"
211       at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
212       at java.lang.reflect.Method.invoke(Method.java:583)
213       at ...
214\end{listing-java}
215
216because the method has been overridden by a non-public class and the
217reflection API, unlike javac, is not able to handle such a case.
218
219While code like that is uncommon in Java, it is typical of ABCL's FFI
220calls. The code above corresponds to the following Lisp code:
221
222\begin{listing-lisp}
223(let ((jar (jnew "java.util.zip.ZipFile" "/path/to/some.jar")))
224  (let ((els (jcall "entries" jar)))
225    (jcall "hasMoreElements" els)))
226\end{listing-lisp}
227
228except that the dynamic dispatch part is not shown.
229
230To avoid such pitfalls, all Java objects in ABCL carry an extra
231field representing the ``intended class'' of the object. That is the class
232that is used first by \code{JAVA:JCALL} and similar to resolve methods;
233the actual class of the object is only tried if the method is not found
234in the intended class. Of course, the intended class is always a superclass
235of the actual class - in the worst case, they coincide. The intended class
236is deduced by the return type of the method that originally returned
237the Java object; in the case above, the intended class of \code{ELS}
238is \code{java.util.Enumeration} because that's the return type of
239the \code{entries} method.
240
241While this strategy is generally effective, there are cases where the
242intended class becomes too broad to be useful. The typical example
243is the extraction of an element from a collection, since methods in
244the collection API erase all types to \code{Object}. The user can
245always force a more specific intended class by using the \code{JAVA:JCOERCE}
246operator.
247
248% \begin{itemize}
249% \item Java values are accessible as objects of type JAVA:JAVA-OBJECT.
250% \item The Java FFI presents a Lisp package (JAVA) with many useful
251%   symbols for manipulating the artifacts of expectation on the JVM,
252%   including creation of new objects \ref{JAVA:JNEW}, \ref{JAVA:JMETHOD}), the
253%   introspection of values \ref{JAVA:JFIELD}, the execution of methods
254%   (\ref{JAVA:JCALL}, \ref{JAVA:JCALL-RAW}, \ref{JAVA:JSTATIC})
255% \item The JSS package (\ref{JSS}) in contrib introduces a convenient macro
256%   syntax \ref{JSS:SHARPSIGN_DOUBLEQUOTE_MACRO} for accessing Java
257%   methods, and additional convenience functions.
258% \item Java classes and libraries may be dynamically added to the
259%   classpath at runtime (JAVA:ADD-TO-CLASSPATH).
260% \end{itemize}
261
262\subsubsection{Calling Java class static methods}
263
264Like with non-static methods, references to static methods can be acquired
265by using the \code{JAVA:JMETHOD} primitive. In order to call this method,
266it's not possible to use the \code{JAVA:JCALL} primitive however: there's a
267separate API to retrieve a reference to static methods. This
268primitive is called \code{JAVA:JSTATIC}.
269
270Like \code{JAVA:JCALL}, \code{JAVA:JSTATIC} supports dynamic dispatch by
271passing the name of the method as a string instead of passing a method reference.
272The parameter values should be values to pass in the function call instead of
273a specification of classes for each parameter.
274
275\subsubsection{Parameter matching for FFI dynamic dispatch}
276
277The algorithm used to resolve the best matching method given the name
278and the arguments' types is the same as described in the Java Language
279Specification. Any deviation should be reported as a bug.
280
281% ###TODO reference to correct JLS section
282
283\subsubsection{Instantiating Java objects}
284
285Java objects can be instantiated (created) from Lisp by calling
286a constructor from the class of the object to be created. The same way
287\code{JAVA:JMETHOD} is used to acquire a method reference, the
288\code{JAVA:JCONSTRUCTOR} primitive can be used to acquire a constructor
289reference. It's arguments specify the types of arguments of the constructor
290method the same way as with \code{JAVA:JMETHOD}.
291
292The constructor can't be passed to \code{JAVA:JCALL}, but instead should
293be passed as an argument to \code{JAVA:JNEW}.
294
295\section{Lisp from Java}
296
297In order to access the Lisp world from Java, one needs to be aware
298of a few things. The most important ones are listed below.
299
300\begin{itemize}
301\item All Lisp values are descendants of LispObject.java
302\item In order to
303\item Lisp symbols are accessible via either directly referencing the
304  Symbol.java instance or by dynamically introspecting the
305  corresponding Package.java instance.
306\item The Lisp dynamic environment may be saved via
307  \code{LispThread.bindSpecial(Binding)} and restored via
308  \code{LispThread.resetSpecialBindings(Mark)}.
309\item Functions may be executed by invocation of the
310  Function.execute(args [...]) 
311\end{itemize}
312
313\subsection{Lisp FFI}
314
315FFI stands for "Foreign Function Interface" which is the phase which
316the contemporary Lisp world refers to methods of "calling out" from
317Lisp into "foreign" languages and environments.  This document
318describes the various ways that one interacts with Lisp world of ABCL
319from Java, considering the hosted Lisp as the "Foreign Function" that
320needs to be "Interfaced".
321
322\subsection{Calling Lisp from Java}
323
324Note: As the entire ABCL Lisp system resides in the org.armedbear.lisp
325package the following code snippets do not show the relevant import
326statements in the interest of brevity.  An example of the import
327statement would be
328
329\begin{listing-java}
330  import org.armedbear.lisp.*;
331\end{listing-java}
332
333to potentially import all the JVM symbol from the `org.armedbear.lisp'
334namespace.
335
336Per JVM, there can only ever be a single Lisp interpreter.  This is
337started by calling the static method `Interpreter.createInstance()`.
338
339\begin{listing-java}
340  Interpreter interpreter = Interpreter.createInstance();
341\end{listing-java}
342
343If this method has already been invoked in the lifetime of the current
344Java process it will return null, so if you are writing Java whose
345life-cycle is a bit out of your control (like in a Java servlet), a
346safer invocation pattern might be:
347
348\begin{listing-java}
349  Interpreter interpreter = Interpreter.getInstance();
350  if (interpreter == null) {
351    interpreter = Interpreter.createInstance();
352  }
353\end{listing-java}
354
355
356The Lisp \code{eval} primitive may be simply passed strings for evaluation,
357as follows
358
359\begin{listing-java}
360  String line = "(load \"file.lisp\")";
361  LispObject result = interpreter.eval(line);
362\end{listing-java}
363
364Notice that all possible return values from an arbitrary Lisp
365computation are collapsed into a single return value.  Doing useful
366further computation on the ``LispObject'' depends on knowing what the
367result of the computation might be, usually involves some amount
368of \code{instanceof} introspection, and forms a whole topic to itself
369(c.f. [Introspecting a LispObject])
370
371Using \code{eval} involves the Lisp interpreter.  Lisp functions may
372be directly invoked by Java method calls as follows.  One simply
373locates the package containing the symbol, then obtains a reference to
374the symbol, and then invokes the \code{execute()} method with the
375desired parameters.
376
377\begin{listing-java}
378    interpreter.eval("(defun foo (msg) (format nil \"You told me '~A'~%\" msg))");
379    Package pkg = Packages.findPackage("CL-USER");
380    Symbol foo = pkg.findAccessibleSymbol("FOO");
381    Function fooFunction = (Function)foo.getSymbolFunction();
382    JavaObject parameter = new JavaObject("Lisp is fun!");
383    LispObject result = fooFunction.execute(parameter);
384    // How to get the "naked string value"?
385    System.out.println("The result was " + result.writeToString());
386\end{listing-java}
387
388If one is calling an primitive function in the CL package the syntax
389becomes considerably simpler.  If we can locate the instance of
390definition in the ABCL Java source, we can invoke the symbol directly.
391For instnace, to tell if a `LispObject` contains a reference to a symbol.
392
393\begin{listing-java}
394    boolean nullp(LispObject object) {
395      LispObject result = Primitives.NULL.execute(object);
396      if (result == NIL) { // the symbol 'NIL' is explicity named in the Java
397                           // namespace at ``Symbol.NIL''
398                           // but is always present in the
399                           // localnamespace in its unadorned form for
400                           // the convenience of the User.
401        return false;
402      }
403      return true;
404   }
405\end{listing-java}
406
407\subsubsection{Introspecting a LispObject}
408\label{topic:Introspecting a LispObject}
409
410We present various patterns for introspecting an an arbitrary
411`LispObject` which can represent the result of every Lisp evaluation
412into semantics that Java can meaningfully deal with.
413
414\subsubsection{LispObject as \code{boolean}}
415
416If the LispObject a generalized boolean values, one can use
417\code{getBooleanValue()} to convert to Java:
418
419\begin{listing-java}
420     LispObject object = Symbol.NIL;
421     boolean javaValue = object.getBooleanValue();
422\end{listing-java}
423
424Although since in Lisp, any value other than NIL means "true"
425(so-called generalized Boolean), the use of Java equality it quite a
426bit easier to type and more optimal in terms of information it conveys
427to the compiler would be:
428
429\begin{listing-java}
430    boolean javaValue = (object != Symbol.NIL);
431\end{listing-java}
432
433\paragraph{LispObject is a list}
434
435If LispObject is a list, it will have the type `Cons`.  One can then use
436the \code{copyToArray} to make things a bit more suitable for Java
437iteration.
438
439\begin{listing-java}
440    LispObject result = interpreter.eval("'(1 2 4 5)");
441    if (result instanceof Cons) {
442      LispObject array[] = ((Cons)result.copyToArray());
443      ...
444    }
445\end{listing-java}
446   
447A more Lispy way to iterated down a list is to use the `cdr()` access
448function just as like one would traverse a list in Lisp:;
449
450\begin{listing-java}
451    LispObject result = interpreter.eval("'(1 2 4 5)");
452    while (result != Symbol.NIL) {
453      doSomething(result.car());
454      result = result.cdr();
455    }
456\end{listing-java}
457
458\chapter{Implementation Dependent Extensions}
459
460As outlined by the CLHS ANSI conformance guidelines, we document the
461extensions to the Armed Bear Lisp implementation made accessible to
462the user by virtue of being an exported symbol in the JAVA, THREADS,
463or EXTENSIONS packages.
464
465\section{JAVA}
466
467\subsection{Modifying the JVM CLASSPATH}
468
469The JAVA:ADD-TO-CLASSPATH generic functions allows one to add the
470specified pathname or list of pathnames to the current JVM classpath
471allowing the dynamic loading of JVM objects:
472
473\begin{listing-lisp}
474CL-USER> (add-to-classpath "/path/to/some.jar")
475\end{listing-lisp}
476
477\subsection{API}
478
479% include autogen docs for the JAVA package.
480\include{java}
481
482\section{THREADS}
483
484Multithreading
485
486\subsection{API}
487
488% include autogen docs for the THREADS package.
489\include{threads}
490
491\section{EXTENSIONS}
492
493The symbols in the EXTENSIONS package (nicknamed ``EXT'') constitutes
494extensions to the ANSI standard that are potentially useful to the
495user.  They include functions for manipulating network sockets,
496running external programs, registering object finalizers, constructing
497reference weakly held by the garbage collector and others.
498
499See \ref{Extensible Sequences} for a generic function interface to
500the native JVM contract for \code{java.util.List}.
501
502\subsection{API}
503
504% include autogen docs for the EXTENSIONS package.
505\include{extensions}
506
507\chapter{Beyond ANSI}
508
509Naturally, in striving to be a useful contemporary Common Lisp
510implementation, ABCL endeavors to include extensions beyond the ANSI
511specification which are either widely adopted or are especially useful
512in working with the hosting JVM.
513
514\section{Implementation Dependent}
515\begin{enumerate}
516  \item Compiler to JVM 5 bytecode
517  \item Pathname extensions
518\end{enumerate}
519
520\section{Pathname}
521
522We implment an extension to the Pathname that allows for the
523description and retrieval of resources named in a URI scheme that the
524JVM ``understands''.  Support is built-in to the ``http'' and
525``https'' implementations but additional protocol handlers may be
526installed at runtime by having JVM symbols present in the
527sun.net.protocol.dynmamic pacakge. See [JAVA2006] for more details.
528
529ABCL has created specializations of the ANSI Pathname object to
530enable to use of URIs to address dynamically loaded resources for the
531JVM.  A URL-PATHNAME has a corresponding URL whose cannoical
532representation is defined to be the NAMESTRING of the Pathname.
533
534PATHNAME : URL-PATHNAME : JAR-PATHNAME
535
536Both URL-PATHNAME and JAR-PATHNAME may be used anu where will a
537PATHNAME is accepted witht the following caveats
538
539A stream obtained via OPEN on a URL-PATHNAME cannot be the target of
540write operations.
541
542No canonicalization is performed on the underlying URI (i.e. the
543implementation does not attempt to compute the current name of the
544representing resource unless it is requested to be resolved.)  Upon
545resolution, any cannoicalization procedures followed in resolving the
546resource (e.g. following redirects) are discarded. 
547
548The implementation of URL-PATHNAME allows the ABCL user to laod dynamically
549code from the network.  For example, for Quicklisp.
550
551\begin{listing-lisp}
552  CL-USER> (load "http://beta.quicklisp.org/quicklisp.lisp")
553\end{listing-lisp}
554
555will load and execute the Quicklisp setup code.
556
557\ref{XACH2011}
558         
559\section{Extensible Sequences}
560
561\ref{RHODES2007}
562
563The SEQUENCE package fully implements Christopher Rhodes' proposal for
564extensible sequences.  These user extensible sequences are used
565directly in \code{java-collections.lisp} provide these CLOS
566abstractions on the standard Java collection classes as defined by the
567\code{java.util.List} contract.
568
569This extension is not automatically loaded by the implementation.   It
570may be loaded via:
571
572\begin{listing-lisp}
573CL-USER> (require 'extensible-sequences)
574\end{listing-lisp}
575
576\section{Extensions to CLOS}
577
578There is an additional syntax for specializing the parameter of a
579generic function on a java class, viz. \code{(java:jclass CLASS-STRING)}
580where \code{CLASS-STRING} is a string naming a Java class in dotted package
581form.
582
583For instance the following specialization would perhaps allow one to
584print more information about the contents of a java.util.Collection
585object
586
587\begin{listing-lisp}
588(defmethod print-object ((coll (java:jclass "java.util.Collection"))
589                         stream)
590  ;;; ...
591)
592\end{listing-lisp}
593
594If the class had been loaded via a classloader other than the original
595the class you wish to specialize on, one needs to specify the
596classloader as an optional third argument.
597
598\begin{listing-lisp}
599
600(defparameter *other-classloader*
601  (jcall "getBaseLoader" cl-user::*classpath-manager*))
602 
603(defmethod print-object ((device-id (java:jclass "dto.nbi.service.hdm.alcatel.com.NBIDeviceID" *other-classloader*))
604                         stream)
605  ;;; ...
606)
607\end{listing-lisp}
608
609\section{Extensions to the Reader}
610
611We implement a special hexadecimal escape sequence for specifying
612characters to the Lisp reader, namely we allow a sequences of the form
613\# \textbackslash Uxxxx to be processed by the reader as character whose code is
614specified by the hexadecimal digits ``xxxx''.  The hexadecimal sequence
615must be exactly four digits long, padded by leading zeros for values
616less than 0x1000.
617
618Note that this sequence is never output by the implementation.  Instead,
619the corresponding Unicode character is output for characters whose
620code is greater than 0x00ff.
621
622\section{ASDF}
623
624asdf-2.017 is packaged as core component of ABCL.  By default, ASDF is
625not loaded, as it relies on the CLOS subsystem which can take a bit of
626time to initialize.
627
628\begin{listing-lisp}
629CL-USER> (require 'asdf)
630\end{listing-lisp}
631
632\chapter{Contrib}
633
634\section{abcl-asdf}
635
636Allow ASDF system definition which dynamically loads JVM artifacts
637such as jar archives via a Maven encapsulation.
638
639ASDF components added:  JAR-FILE, JAR-DIRECTORY, CLASS-FILE-DIRECTORY
640and MVN.
641
642\section{asdf-install}
643
644An implementation of ASDF-INSTALL.  Superceded by Quicklisp (qv.)
645
646\section{asdf-jar}
647
648ASDF-JAR provides a system for packaging ASDF systems into jar
649archives for ABCL.  Given a running ABCL image with loadable ASDF
650systems the code in this package will recursively package all the
651required source and fasls in a jar archive.
652
653\section{jss}
654
655Java Syntax sucks, so we introduce the \#" macro.
656
657
658\chapter{History}
659
660ABCL was originally the extension language for the J editor, which was
661started in 1998 by Peter Graves.  Sometime in 2003, it seems that a
662lot of code that had previously not been released publically was
663suddenly committed that enabled ABCL to be plausibly termed an ANSI
664Common Lisp implementation. 
665
666In 2006, the implementation was transferred to the current
667maintainers, who have strived to improve its usability as a
668contemporary Common Lisp implementation.
669
670In 201x, with the publication of this Manual explicitly stating the
671conformance of Armed Bear Common Lisp to ANSI, we release abcl-1.0.
672
673
674
675
676\section{References}
677
678[Java2000]:  A New Era for Java Protocol Handlers.
679\url{http://java.sun.com/developer/onlineTraining/protocolhandlers/}
680
681[Xach2011]:  Quicklisp:  A system for quickly constructing Common Lisp
682libraries.  \url{http://www.quicklisp.org/}
683
684
685\end{document}
686
687% TODO
688%   1.  Create mechanism for swigging DocString and Lisp docs into
689%       sections.
690
Note: See TracBrowser for help on using the repository browser.