All Scheme functions and source files are invisibly compiled into internal Java byte-codes. (A traditional interpreter is used for macro-expansion. Kawa used to also interpret “simple” expressions in interactive mode, but always compiling makes things more consistent, and allows for better stack traces on errors.)
To save speed when loading large Scheme source files, you probably want to pre-compile them and save them on your local disk. There are two ways to do this.
You can compile a Scheme source file to a single archive file.
You do this using the compile-file
function.
The result is a single file that you can move around and load
just like the .scm
source file. You just specify the name
of the archive file to the load
procedure.
Currently, the archive is a "zip" archive and has extension ".zip";
a future release will probably use "Java Archive" (jar) files.
The advantage of compiling to an archive is that it is simple
and transparent.
Alternatively, you can compile a Scheme source file to a
collection of ‘.class
’ files.
You then use the standard Java class loading mechanism to load the code.
The compiled class files do have to be installed somewhere
in the CLASSPATH
.
Invoking ‘kawa
’ (or ‘java kawa.repl
’) with
the ‘-C
’ flag will compile
a ‘.scm
’ source file into one or more ‘.class
’ files:
kawa --main -C myprog.scm
You run it as follows:
kawa [-doutdirectory
] [-Pprefix
] [-Ttopname
] [--main | --applet | --servlet] -Cinfile
...
Note the ‘-C
’ must come last, because ‘Kawa
’ processes the
arguments and options in order,
Here:
-C infile
...
The Scheme source files we want to compile.
-d outdirectory
The directory under which the resulting ‘.class
’ files will be.
The default is the current directory.
-P prefix
A string to prepend to the generated class names. The default is the empty string.
-T topname
The name of the "top" class - i.e. the one that contains the code
for the top-level expressions and definitions.
The default is generated from the infile
and prefix
.
--main
Generate a main
method so that the resulting "top" class can
be used as a stand-alone application. See Application compilation.
--applet
The resulting class inherits from java.applet.Applet
,
and can be used as an applet. See Applet compilation.
--servlet
The resulting class implements javax.servlet.http.HttpServlet
,
and can be used as a servlet in a servlet container like Tomcat.
When you actually want to load the classes, the outdirectory
must be in your ‘CLASSPATH
’.
You can use the require
syntax or the load
function to load the code,
by specifying the top-level class, either as a file name
(relative to outdirectory
) or as a class name.
E.g. if you did:
kawa -d /usr/local/share/java -P my.lib. -T foo -C foosrc.scm
you can use either:
(require my.lib.foo)
or:
(load "my.lib.foo")
Using require
is preferred as it imports the definitions
from my.lib.foo
into the compile-time environment,
while load
only imports the definitions into the run-time environment.
If you are compiling a Scheme source file (say ‘foosrc.scm
’)
that uses macros defined in some other file (say ‘macs.scm
’),
you need to make sure the definitions are visible to the compiler.
One way to do that is with the ‘-f
’:
kawa -f macs.scm -C foosrc.scm
Many of the options described earlier are
relevant when compiling. Commonly used options include language selection,
the --warn-xxx
options, and --full-tailcalls
.
Procedure: compile-file
source-file
compiled-archive
Compile the
source-file
, producing a.zip
archivecompiled-file
.For example, to byte-compile a file ‘
foo.scm
’ do:(compile-file "foo.scm" "foo")This will create ‘
foo.zip
’, which contains byte-compiled JVM.class
files. You can move this file around, without worrying about class paths. To load the compiled file, you can laterload
the named file, as in either(load "foo")
or(load "foo.zip")
. This should have the same effect as loading ‘foo.scm
’, except you will get the faster byte-compiled versions.
Many Java projects use Ant
for building Java projects. Kawa includes a <kawac>
Ant task that simplifies compiling Kawa source files to classes.
See the build.xml
in the Kawa source distribution for examples.
See the kawac
task documentation for details.
A Java application is a Java class with a special method
(whose name is main
). The application can be invoked directly
by naming it in the Java command.
If you want to generate an application from a Scheme program,
create a Scheme source file with the definitions you need, plus
the top-level actions that you want the application to execute.
For example, assuming your Scheme file is
MyProgram.scm
, you have two ways at your disposal to
compile this Scheme program to a standalone application:
Compile
in the regular way described in the previous section, but add the
--main
option.
kawa --main -C MyProgram.scm
The --main
option will compile all Scheme programs
received in arguments to standalone applications.
Compile
in the regular way decribed in the previous section, but add the
main: #t
module compile option to your module.
;; MyProgram.scm (module-name <myprogram>) (module-compile-options main: #t)
kawa -C MyProgram.scm
This way you can compile multiple Scheme programs at once, and still control which one(s) will compile to standalone application(s).
Both methods will create a MyProgram.class
which you can either
load
(as described in the previous section), or invoke as an application:
java MyProgram [args
]
Your Scheme program can access the command-line arguments args
by using the global variable ‘command-line-arguments
’,
or the R6RS function ‘command-line
’.
If there is no explicit module-export
in a module compiled
with --main
then no names are exported. (The default
otherwise is for all names to be exported.)
An applet is a Java class that inherits from java.applet.Applet
.
The applet can be downloaded and run in a Java-capable web-browser.
To generate an applet from a Scheme program, write the Scheme
program with appropriate definitions of the functions ‘init
’,
‘start
’, ‘stop
’ and ‘destroy
’. You must declare these
as zero-argument functions with a <void>
return-type.
Here is an example, based on the scribble applet in Flanagan’s "Java Examples in a Nutshell" (O’Reilly, 1997):
(define-private last-x 0) (define-private last-y 0) (define (init) :: void (let ((applet (this))) (applet:addMouseListener (object (java.awt.event.MouseAdapter) ((mousePressed e) (set! last-x (e:getX)) (set! last-y (e:getY))))) (applet:addMouseMotionListener (object (java.awt.event.MouseMotionAdapter) ((mouseDragged e) (let ((g (applet:getGraphics)) (x (e:getX)) (y (e:getY))) (g:drawLine last-x last-y x y) (set! last-x x) (set! last-y y))))))) (define (start) :: void (format #t "called start.~%~!")) (define (stop) :: void (format #t "called stop.~%~!")) (define (destroy) :: void (format #t "called destroy.~%~!"))
You compile the program with the ‘--applet
’ flag in addition to the
normal ‘-C
’ flag:
java kawa.repl --applet -C scribble.scm
You can then create a ‘.jar
’ archive containing your applet:
jar cf scribble.jar scribble*.class
Finally, you create an ‘.html
’ page referencing your applet
and its support jar
s:
<html><head><title>Scribble testapp</title></head> <body><h1>Scribble testapp</h1> You can scribble here: <br> <applet code="scribble.class" archive="scribble.jar, kawa-3.1.1.jar" width=200 height=200> Sorry, Java is needed.</applet> </body></html>
The problem with using Kawa to write applets is that the Kawa .jar
file is quite big, and may take a while to download over a network connection.
Some possible solutions:
Try to strip out of the Kawa .jar
any classes your
applet doesn’t need.
Java 2 provides a mechanism to install a download extension.
Consider some alternative to applets, such as Java Web Start.