A version of SILK with generic functions
SILK is a compact scheme dialect written in Java that can add powerful
capabilites to Java applications, such as:
- User preferences and extensions.
- Playfully interactive debugging, development and testing.
- Specialized languages, for example, for GUI layout.
- Java knowledgable Make facility - SILK compiles itself.
- Runtime Java code generation - new classes from old.
- Automatic recompilation of classes as they are loaded.
The original version of SILK was
written by Peter Norvig. Tim Hickey
added JLIB
a system for easily writing applets.
Generic functions have been added to SILK.
A paper (html version,
Postscript version)
describing them
has been submitted to Reflection '99. You can
also see a live demo of many
of the examples presented in the talk.
An earlier version of SILK was described at the
Lisp in the Main Stream conference, November 1998:
Scheme API's [Incomplete]:
Running SILK
SILK and your application can have access to each other if they are in
the same classpath, or more specifically, come from the same
ClassLoader.
SILK's operation and capabiliies depened on which ClassLoader is
used and how SILK is to run. See Example
Scripts below.
Either Down load everything, or if you
are at BBN, you can build SILK from CVS.
Everything works in JDK 1.2. However most of SILK will run fine in
JDK 1.1 either as an application or an applet. What doesn't currently
work in JDK 1.1 is related to ClassLoaders.
A JDK 1.2 version of SILK is provided in jar/scheme.jar which also includes scheme
software in *.scm files.with generic function
extensions in src/generic. As the live demo demonstrates, this
jar will also work in JDK 1.1, but some of the classes will not work.
You can build your own version using one of several tiny
scripts:
Here are some interesting variants to use or copy and customize:
Main classes
The two main classes of SILK are silk.Scheme and silk.EmbeddedScheme. Their
main(String[] args) method treat the args as a set of
files that are loaded. Then a read-eval-print loop is started.
The class silk.EmbeddedScheme automatically load
"src/generic/load.scm", while silk.Scheme does not.
It also static methods that can be useful when SILK is used as part of
an application.
File lookup
The file opening done command line processing, and by
(open-input-file) and (load) has been generalized to
allow relative file naming and use of URL's. This allows *.scm files
to be added to a jar file without modification. The lookup strategy
for finding a file named name is to try each of the following
until one succeeds:
- Open the file name (application only).
- Prepend each of the elements of the list scheme.roots
to name and open that file (application only).
- Use the classloader to open name as a resource, for
example in a *.jar file.
- Open name as a URL.
- Open name relative to documentbase (applet only).
When silk.Scheme starts, it looks for a "scheme.eval" system
property. The value of this property is evaluated just after the
primitives are loaded. The primary purpose of this is to set the
scheme.roots variable. So for example, the way i use it is:
java "-Dscheme.eval=(set! scheme.roots '(d:/java/elf d:/java/silk))" silk.EmbeddedScheme src/classgen/compile.scm ulps/jdbc.scm
So, the file compile.scm is found in the scheme.jar
and ulps/jdbc.scm is found relative to
d:/java/elf.
Other information
Known problems
- JDK 1.1 limitations
- The core of SILK with generic functions is supported in JDK 1.1.
However, the following features are missing or restricted:
- Because of access limitations, (describe object) only describes
public fields. In JDK 1.2, all the fields of an object can be seen.
- (get-field) and (set-field) are similarly limited.
- build.CompilingClassLoader and silk.SchemeCLassLoader are
currenly JDK 1.2 specific.
- Classes that import from the package javax.swing will not
compile and will not theirfore be in jar/scheme.jar. elf.GCMonitor
is such an example class.
- Applet and JLIB issues
- SILK will run in an Applet using JLIB. See the examples in the
applet directory.
However, to use the generic extensions, you must currently put the
applet's .html and .scm file in the src/generic directory.
To use JLIB in the generic environment, use the libraries/Java2.scm version which has
been changed slightly to avoid name conflicts. For a working example,
see src/scheme/schemegui.html.
- Applet loads slow
- On a 266 MHz Pentium II, in Netscape 4.5, the time it takes to
start scheme is about 20 to 35 seconds. When using scheme as an
application, it takes about a second in JDK 1.2 and JDK 1.1.6.
On an iteration benchmark (essentially a consing benchmark):
(define (f x) (if (= x 0) #f (f (- x 1))))
(time (f 1000000))
Netscape seems to be twice as slow ast JDK 1.2. JDK 1.1.6 is slightly
faster. Including this difference, we still have a factor of 10 to
account for.
- Java Strings
-
Strings in Scheme are represented as Java char[]. When Scheme calls a
Java method the expects a String, a Scheme string or symbol is
converted into a Java String. When a Java method returns a String as
a value, it is converted into a Scheme symbol (since they are
immutable).
Thus (toString x) converts x to a symbol, rather than a
string. So if you want a string, say (symbol->string (toString
x)) or (string-append x).
- String memory leak
- A memory leak occurs because Java
Strings are converted into Scheme symbols, which are interned. So, a
application such as src/elf/GCmonitor.scm
which essentially generates a random string every half second can
continually grow. GCMonior grows at 1MB/hour. The Java version does not grow.
- Primitive arrays
- A Scheme array is represented as a Java Object[]. Primitive
arrays, such as String[] can be constructed using
(java-vector CLASS element1 ...) which constructs an array
with elements of type CLASS containing the elements
element1 .... The procedure (make-java-vector CLASS
SIZE) will make a particular class of Java array of size SIZE.
The procedures(vector-ref) and (vector-set!) can be
used to get or set an array element of any type of array.
- Primitive values
- However, to insert an element into an primitive array, one must construct
an appopriae value. The Scheme side of SILK supports only a few
primitive types: boolean, char, int, and double. So, filling an array
of a primitive type can be tricky. For example, to produce a value
that would go into a byte[], you need to construct a Byte in Scheme.
Byte has two constructors:
public Byte(String);
public Byte(byte);
The second version can't be used in Scheme unless you have a Byte
already. This forces you to use the kludge:
(import "java.lang.Byte")
(Byte (string-append 3)) ; returns a Byte representing 3.
See the class silk.Coerce which
provides conversion between primitive types.
- Method lookup ambiguities
- Generic function method lookup uses the runtime types of its
arguments to decide which method to invoke. There are some ambiguity
to this process. For example, a null argument provides no runtime
type information. However, while the lookup process works generally,
it has some flaws that cause it to fail occasionally. The workaround
for such cases is to use (method)to select the method you
want to invoke.
- Few numeric types
- SILK's Scheme side only support Integer and Double as numeric
types. However, SILK's implementation will somewhat handle the other
types. For example:
> (import "java.lang.Shor")
importing java.lang.Short in 40 ms.
> (+ (Short "3") (Short "4"))
7.0
Precision can be lost, so when dealing with Long's, convert them to
Double's first. Here's code that deals with file cration times:
(define (last-modified file)
;; KRA 05AUG99: We need this kludge because SILK does not deal with Longs.
(Double (toString (lastModified file))))
(define (needs-recompile class-file java-file)
(if (not (exists java-file)) (error java-file " does not exist!"))
(or (not (exists class-file))
(> (last-modified java-file) (last-modified class-file))))
Multiple Schemes
It is possible to have multiple Schemes per a Java application.
However, each one will read in primitives and generic extensions.
There is currently no way for Schemes to share the same global environment.
Release notes
Sept 20, 1999
- This version is again JDK 1.1 compatible.
- build\bootstrap1.bash for building in JDK 1.1.
- Rewrite of class build.CompilingClassLoader.
- Get src/scheme/schemegui.html working again.
Sept 15, 1999
- You can now provide input and output streams to be used by
silk.Scheme.
- SILK now under CVS and builds itself using build.CompilingClassLoader.
- New classes:
- build.CompilingClassLoader - Compile as you run your application.
- silk.EmbeddedScheme - Convenient for hiding Scheme inside your
application.
- silk.Coerce - conversion methods between primitive types.
- silk.SchemeClassLoader
- silk.ExitException - thrown by (exit).
- New Scheme files:
- classgen/compile.scm - extensive compiling support.
- build/update.scm - used to build silk.
- generic/trace.scm - very simple trace facility.
June 15, 1999
- Draft Reflection '99 slides and demo applet.
- elf/GCMonitor.scm and efl/jdbc.scm examples
-
May 21, 1999
- Less verbose (describe) of a class.
- (get-field) (set-field)
(get-static) and (set-static) field accessors now work with private
fields when JDK 1.2 is used.
- Loading src/classgen/compile.scm provides:
- (compile-file file) - compiles a Java file.
- (recompile directory) - recompiles Java files in a directory.
This assumes the class file is also in the directory. Only Java files
that need compiliation are compiled.
May 3, 1999
- (describe) generic function that accesses private fields in JDK
1.2. See src/generic/describe.scm.
- Fixed array bound bug in SchemeUtil.chr().
April 28, 1999
- Final draft of reflection99 paper.
- (define-method) for defining methods in scheme.
- (vector-set!) works with any class of vector.
- src/elf directory has GCMonitor example.
March 28, 1999
- Performance of (import) improved by about 20%.
- Preliminary version of macroexpand-all provided in
src/generic/macroexpand.scm.
- The standard SLIB library is provided in src/slib. To use it,
edit the src/slib/silk.init file appropriately. I've tested
(require 'pretty-print). Other files may work but may need
to be checked for case sensitivity.