Table of Contents
Requires J2SE 1.2 or greater.
Go to llava.org
Unzip it resulting in llava.jar
Startup: java -jar llava.jar
Or: java -cp llava.jar org.llava.Llava
Note, you should start up with the main class, org.llava.Llava, if you want to import llava files (i.e., *.lva) from locations on your classpath
Examples: check out the libraries in org.llava.lib in llava.jar
Optional source code: llavasrc.zip
For a full list of improvements see the TODO list
where category may be "syntax" or "procedure", and where location specifies the package containing the feature. If the location is contained inside (import location) then the package must be imported in order to use the feature. Otherwise the feature is already imported and location simply gives the package location of the feature.
(* 5 8) ==> 40
means that the expression (* 5 8) evalutes to the object 40.
(new 'java.util.Date) (new 'java.lang.Integer 3)
Note: if the class has been imported then you can call the constructor directly:
(import java.lang.Integer) (Integer 3)
or you can use new shorthand:
(new 'Integer 3)
Also note, even though new is syntax, all arguments are evaluated.
(-i 'getMonth (new 'java.util.Date))
Note, virtual methods may use standard (and preferred) prefix generic procedure notation:
(methodName targetObj obj ...)
If methodName has not been bound or has been bound to a generic procedure then this is equivalent to:
(-i methodName targetObj obj ...)
The above example is best written as:
(getMonth (new 'java.util.Date))
(-si 'parseInt 'java.lang.Integer "ff" 16)
Note, static methods may be called directly after their class has been imported. The above example can then be written:
(import java.lang.Integer) (parseInt "ff" 16)
(Integer.parseInt "ff" 16)
(-f 'minor (new 'org.omg.CORBA.BAD_PARAM)) ; get (-f 'minor (new 'org.omg.CORBA.BAD_PARAM) 56) ; set
Note, virtual fields may be accessed directly after their class has been imported. The above example can then be written:
(import org.omg.CORBA.SystemException) (minor (new 'org.omg.CORBA.BAD_PARAM)) (minor! (new 'org.omg.CORBA.BAD_PARAM) 56)
(-sf 'out 'java.lang.System) ; get (-sf 'out 'java.lang.System 0) ; set - and an error since Field is final
Note, static fields may be accessed directly after their class has been imported. The above example can then be witten as:
(import java.lang.System) (out) (out! 0)
Binds a procedure to variable that, when called, will first use Java reflection to find a method on the first argument that matches the types of the remaining arguments. If one is found it is invoked. Otherwise body is executed after the arguments are bound.
(define (name requiredArg1 requiredArg2 . optionalArgs) body)
Defines a generic procedure which, when called as:
(name 1 2)
will first try:
(-i name 1 2)
If no Java method is found (and invoked) then the generic procedure body will be called:
((lambda (requiredArg1 requiredArg2 . optionalArgs) body) 1 2)
(define (getMessage x) (cdr x)) (getMessage '(1 2 3)) ==> (2 3) (getMessage (new 'java.lang.Exception "test msg")) ==> test msg
Binds a procedure to variable that, when called, will always execute body after the arguments are bound.
(define name (lambda (requiredArg1 requiredArg2 . optionalArgs) body)
defines a generic procedure which, when called as:
(name 1 2)
will always call:
((lambda (requiredArg1 requiredArg2 . optionalArgs) body) 1 2)
(let ((bomb 1)) (define (demo) (try (begin (if (< bomb 0) (throw (new 'java.lang.RuntimeException "Give up!"))) (list "Normal result is: " (/ 2 bomb))) (catch (java.lang.ArithmeticException e) (list "Arithmetic: " e)) (catch (java.lang.Exception e) (list "Exception: " e)) (finally (set! bomb (- bomb 1)))))) (demo) ==> (Normal result is: 2) (demo) ==> (Arithmetic: java.lang.ArithmeticException: / by zero) (demo) ==> (Exception: java.lang.RuntimeException: Give up!)
Note, it is not necessary to give the full classpath in the catch clause if the class has been imported.
Also see org.llava.lib.Thread
package and import create, load and reference namespaces.
import searches the roots of CLASSPATH for files ending in *.lva with the given path and name. If one is found it is loaded.
The first form in an imported *.lva file must be a package declaration.
The namespace of the imported package is added to the end of list of namespaces imported into the package current when the import statement is evaluated.
Variable definitions and references occur with respect to the current package. If the variable is not found in the current package the import list is searched in order. If not found in the import list the top-level org.llava package is searched last.
If symbol names a *.class file instead then it results in generic procedure definitions for all virtual methods and results in non-generic procedure definitions of static methods and fields (both static and virtual):
(System.out) ;; both equivalent to (-sf 'out 'java.lang.System)
(Integer.TYPE) ;; both equivalent to (-sf 'TYPE 'java.lang.Integer)
(java.lang.Integer.parseInt "ff" 16)
(Integer.parseInt "ff" 16) ;; both equivalent to (-si 'parseInt 'java.lang.Integer "ff" 16)
Note: these definitions take places in the current package. In the future this will change such that a llava namespace is created, the definitions will be placed in that package and that package will be added to the import list of the current package.
(instanceof (new 'java.util.Hashtable) 'java.util.Hashtable) ==> true
Note, like new, the full classpath is not necessary if the class has been imported:
(import java.util.Hashtable) (instanceof (new 'Hashtable) 'Hashtable) ==> true
llava is statically scoped.
llava is dynamically typed.
llava is properly tail-recursive.
Arguments to llava procedures are passed by value.
Like Scheme, llava is a Lisp-1, meaning that functions and variables are stored in the same namespaces (which is different from Common Lisp, a Lisp-2, which uses different namespaces for functions and variables).
llava uses a parenthesized prefix notation for programs and (other) data.
The -read procedure performs syntactic as well as lexical decomposition of the data it reads.
The following llava syntax (i.e., special forms) is immutable.
From (or similar to) Scheme:
begin define define-final lambda let letrec quote set!
llava is case-sensitive. For example, Foo is not the same identifier as FOO.
A block comment. Recognized by org.llava.lib.LlavaDoc.
. + - Used in numbers and identifiers, as in R5RS.
( ) Used for grouping and to notate lists, as in R5RS.
' Indicates literal data, as in R5RS.
` Indicates almost constant data, as in R5RS.
, ,@ Used in conjunction with backquote, as in R5RS.
" Used to delimit strings, as in R5RS.
\ Used in character constants and as an escape within string constants, as in R5RS.
true false These are the boolean constants.
#\ Introduces a character constants, as in R5RS.
#( Introduces a vector constant, as in R5RS.
An expression consisting of a identifier is a variable reference.
Misc., looping, mapping, case.
Like let* but <bindings> happen inside a try and <body> can contain catch and finally.
Known bug: if you bind a variable which shadows an outer variable and uses the value of the outer variable for its initial value the newly bound variable will always have false for a value:
(define (foo b) (list (tlet* ((b b)) b (catch (java.lang.Exception e) -1)) b)) (foo 3) ==> (false 3)
Procedures for handling or generating exceptions.
Useful list utilities.
Generates package and procedure documentation in DocBook format.
Utilities to help with writing macros.
Syntax for defining procedures and variables.
Procedures to manipulate strings.
Starting and killing threads.
Procedures for manipulating vectors.
Common Lisp control features.
Common Lisp macros.
Common Lisp symbols utilities.
Common Lisp sequence procedures.
Generic procedures to walk the contents of a file.
Procedures for looking at package namespaces and their contents.
Reflective display and equality.
Scheme list procedures.
Scheme type predicates.
Scheme string procedures.
llava test utilities.
During the 80s and early 90s I was involved in designing and building Lisp systems. When funding for Lisp became scarce I switched to distributed computing and ended up working for SUN Microsystems on CORBA software in C++ (DOE/NEO). Then Java arrived. At SUN, our group's efforts in C++ were redirected towards Java.
Immediately, upon first using Java, I was struck by the fact that it was fun to program again (vis-a-vis the tedious nature of programming in C++). It was clear that the reason for this joy was Java's higher level ala Lisp. However, once I tasted that level again I was soon disappointed by Java since it lacked many features of Lisp which make working at that level fun and productive.
Then Per Bothner released Kawa, a version of Scheme in Java. Using Kawa I was able to program a little more interactively with Java but I kept running into missing library items. I was continually forced to choose between finding versions of btrees, hash tables, etc., in public-domain Scheme code, or making foreign-function interfaces to Java versions of the same.
I ended up doing more of the later. I automated this with an "import" macro which would automatically wrap all or selected methods of specified Java classes using Java reflection. However, I then kept running into the need to convert between Kawa/Scheme IO, strings, characters, etc., and Java IO, strings, characters, etc. When I wanted to define classes for use in Kawa I either had to write the classes in Java which I could import with my macro, or I could use Meroon or some other Scheme object system. However the Scheme objects did not interoperate with the Java objects so I was back to the non-interoperability of Kawa and Java types again. This lack of interoperability just kept cropping up every step of the way, particularly in exceptions. (I haven't kept track of Kawa, so many of these problems, problems for me at least, may be resolved. Plus, I definitely recommend Kawa where performance is a concern. Kawa is a great system.)
That's when I got the idea for llava.
Why not create a version of Java which uses Lisp syntax (or, more precisely, Lisp's lack of syntax) to write Java classes? Then extend that system to include a few special forms (lambda, set!, if, quote, define-syntax), a read-eval-print loop and support for incremental (re)definition to support interactive program development. In other words, a minimal Lisp system with complete interoperability with Java to leverage the Java Virtual Machine and growing Java class libraries.
Originally my name for llava was JIST: for Java + lIST. I called it JIST (not JISP), to emphasize the combination of Java and lIST capabilities and syntax, rather than give the impression of a Lisp system written in Java.
But JIST (or JISP) didn't sound right. So I went with LAVA: for Lisp + jAVA. But lava.org was already taken so I added an "l" - thus: llava (pronounced either "lava" - like the volcanic stuff - or "yava" - like in Spanish - your choice).
llava is not a Lisp/Scheme system written in Java. llava is access to Java via Lisp syntax and features. The philosophy of llava is: maximum leverage of Java—only add what is missing or cannot be done in Java (e.g., list "syntax" and library, repl, incremental (re)definition, macros).
Copyright (c) 1997 - 2005 Harold Carr (http://haroldcarr.com/)
This work is licensed under a Creative Commons License
llava is not associated with Sun Microsystems.
Java is a trademark of Sun Microsystems.
Last Modified : Fri Aug 5 10:34:46 PDT 2005 by hcarr.