next_inactive up previous


The $\hbox {\tt JLambda}$ Language
version 0311

Ian A. Mason2, David Porter3, Carolyn Talcott4


Contents


Introduction

The Java g2d package was designed and built as a visualization tool for formal reasoning systems such as Maude [1], PVS [2], and SAL [3]. It is a core component of the IOP system [4], an infrastructure for allowing formal reasoning tools to interoperate, and to interact with users in a transparent and illucidating graphical fashion.

The g2d package consists of two intertwined components:

  1. An untyped Scheme-like lexically scoped interpreted language, called $\hbox {\tt JLambda}$, that provides a runtime interface to the Java class library, as well as some specific classes that make up the second component. The language makes very heavy use of Java's built in reflective capabilities. It is designed to be efficient and expressive enough to enable full and faithful use of any built in Java classes.

  2. A Java class hierarchy, called the Glyphish hierarchy [5], inspired by Joel Bartlett's now depricated Ezd package [6], and Java's 2D [7] implementation, that provides $\hbox {\tt JLambda}$ with sufficient built in classes to effectively construct, at runtime, any desired interactive graphical object.

The Glyphish hierarchy has been designed both with several benchmark applications in mind, and with architectural generality. The hierarchy makes heavy use of $\hbox {\tt JLambda}$ language. Closures, in particular, provide a rich language in which to describe control flow, event listeners, and even both static and non-static methods of dynamically created classes.

This paper serves as a reference guide to the $\hbox {\tt JLambda}$ language. In section 2 we provide a detailed overview of the constructs of the $\hbox {\tt JLambda}$ language. In section 3 we briefly describe the Glyphish hierarchy that makes up the second of the two intertwined components of the g2d package. In section 4 we provide commentary on a complete prototypical $\hbox {\tt JLambda}$ program. In section 5 we describe the implementation of the interpreter, and related work. Finally in section 6 we describe the process by which methods and constructors are resolved. The language itself, as well as the Glyphish hierarchy are freely available [8].


The $\hbox {\tt JLambda}$ Language.

The Scheme like $\hbox {\tt JLambda}$ language is a minimalistic, call-by-value, left to right evaluation ordering, lexically scoped language with closures. It has exactly the same underlying primitive data types as Java, and access to all of Java's built in packages and classes, as well as any other Java classes found in the class path.

The syntax of the language is based on the usual Lisp notion of an S-expression. An S-expression is either a string of characters or a List of S-expressions. A list is either empty, or else contains at least one element.

Variables

A string of characters is interpreted as a variable, and is evaluated as follows: First see if it is a static Java field e.g.

java.awt.Color.black,
or
java.awt.geom.GeneralPath.WIND_EVEN_ODD,
if it is, return the current value of that field. Next see if it is bound in the current lexical environment, if it is return its current value. Otherwise look to see if it is bound in the current global environment, if it is return its current value, else throw an unbound variable exception.

Definitions

Global definitions are made using the define form. We provide the usual two Scheme versions. The more general form simply binds the value of <exp> to the identifier <name> (which is not evaluated):

                                 
(define <name>  <exp>)
The more specific function or closure definition version is
  
(define <name> (<param_1> ... <param_N>)  <form>)
with $\mathtt{N} \geq 0$, which binds the name <name> to the corresponding closure, in the global environment. If two define expressions assign to the same variable, the first is lost or overwritten. Thus our define is similar to a set. Note that the latter is just an abbreviation for a variant of the former form. Namely
(define <name> (lambda (<param_1> ... <param_N>)  <form>))
We will discuss closures, and lambda expressions shortly, along with the lexical binding form let.

Primitive Data

Primitive data is represented literally in $\hbox {\tt JLambda}$ by a primitive data expression, which is a list of length two:

(<tag>  <exp>)
Neither the <tag> position nor the <exp> is evaluated. The <tag> should be the name of one of Java's primitive data types:
 boolean  byte  double  char  float  int  long  short
while <exp> should be a Java literal, i.e. a sequence of characters. It is parsed as the appropriate data, for example (int 10) will be parsed as the number 10, a Java int, while (char 'C') and (boolean false) will be parsed as Java's character C, and boolean false, respectively.

If parsing as such fails, then it is the assigned the default zero value of that Java data type (false in the case of a boolean), and a warning to standard error is issued.

The usual Java literals can be used within these primitive data expressions, see section 3.10, pages 19-27, of [9]. A representative sample is given here:

(char 88)         ; evaluates to X
(char 'X')        ; evaluates to X
(char '\130')     ; evaluates to X
(char '\u0058')   ; evaluates to X
(char '\n')       ; evaluates to a new line
(byte 127)        ; evaluates to 127
(byte 128)        ; evaluates to 0 and an error is reported
(int 123456)      ; evaluates to 123456
(int 123456.7)    ; evaluates to 0 and an error is reported
(float 2.5e+7)    ; evaluates to 25000000
(boolean true)    ; evaluates to true
(boolean 0)       ; evaluates to false

It should be pointed out that unadorned strings that consist solely of digits, for example 1234567890, are bonafide variables, and can be bound both lexically and globally. To regard them as numbers they need to be wrapped in a primitive data expression. For example, there is nothing wrong with initializing the global environment via the following sequence of expressions.

(define 0 (int 0))
(define 1 (int 1))
...
(define 100 (int 100))

Numeric Operations

The usual arithmetic operations are provided. These work on numbers and chars, not booleans. These follow the usual Java contortions, see section 5.6, pages 74-76 of [9]. The expressions are evaluated, they are both widened to ints if they are smaller. If one is bigger than an int they are both widened to that type, then the operation is performed.

(- <nexp>)                  
(- <nexp> <nexp>)            
(* <nexp> <nexp>)            
(+ <nexp> <nexp>)            
(/ <nexp> <nexp>)            
(% <nexp> <nexp>)
To complete the arithmetic operations we provide a corresponding narrowing operation:
 
(narrow <nexp> <exp>)
This performs one of Java's 23 primitive narrowing conversions (See section 5.1.3, page 55, of [9]). The expressions are evaluated in left to right order. The second expression <exp> should evaluate to one of the strings: byte, short, char, int, long, double, or float. Indicating the desired primitive data type. The first expression <nexp> should evaluate to a primitive numeric type (including char). The usual loss of precision occurs. Attempting to specify a conversion that is not a narrowing, e.g. (narrow (int 0) "long") will result in an exception being thrown.

Boolean Relations

We provide the usual binary relations on numbers:

(<  <nexp> <nexp>)
(>  <nexp> <nexp>)
(<= <nexp> <nexp>)
(>= <nexp> <nexp>)
Non numeric arguments will cause an exception to be thrown.

Like most versions of Lisp or Scheme, there are two forms of equality. Both forms of equality are total boolean relations defined on all types of data:

(= <exp> <exp>)         
(== <exp> <exp>)
The expressions are evaluated in left to right order. The first form, =, is related to the Scheme or Lisp equals predicate, while the second, ==, is more like eq.

The = expression then returns true if either both values are null, or neither are null and both are booleans of the same value, or both numeric values and are equal, or the second object satisfies the first object's equals method. For example (= (int 0) (long 0)) is true. The == is similar but uses pointer equality in the case that both values are objects.

(== "foo" (object ("java.lang.String" "foo")))  ; false
(= "foo" (object ("java.lang.String" "foo")))   ; true
(= (char 'X') (int 88))                         ; true
(= (char 'X') (boolean false))                  ; false
These first two examples use the $\hbox {\tt JLambda}$ notation for calling Java's contructors. This notation will be explained shortly.

There is also an inequality expression:

(!= <exp> <exp>)
which is the same as (not (= <exp> <exp>)).

Arrays

Arrays may be constructed and manipulated in a direct manner.

Array Construction

There are two forms of array construction, one corresponds to making an empty array of a particular size, the other corresponds to making an array, and assigning its contents. A zeroed array of a particular length is constructed via:

(mkarray  <type>  <nexp>)
whereas an array is constructed and assigned via:
(array  <type>   <exp_1> .... <exp_N>)
where $\mathtt{N} \geq 0$. In both cases <type> is either a primitive data tag or else the full name of a Java class, <type> is not evaluated). In the mkarray case the expression <nexp> should evaluate to an integer expression (or something smaller), and an array of that length is constructed and zeroed, according to the nature of <type> in the usual Java way. In the array case of an array of length N is constructed. There can be zero or more elements <exp_i>. The contents of the constructed array are initialized to be the values of the <exp_i>, evaluated from left to right. Each of the values of the expressions should be able to be considered as an element of the class or type represented by the <tag>. null is allowable in the case that the <tag> names a Java class. Widening is allowable in the case of primitive data, as is upcasting to a interface or a superclass in the case that the <tag> names a Java class. Otherwise an exception will be thrown.

In the following examples a0 will be an array of length 88, that contains that many falses, a1 will name an array of integers of length 3, whereas a2 will name an array of objects of length 1.

(define a0 (mkarray boolean (char 'X')))
(define a1 (array int (char 'X') (byte 3) (short 7)))
(define a2 (array java.lang.Object java.lang.System.err))
(define a3 (array [I a1 a1)))
(define a4 (array [Ljava.lang.Object; a2 a2 a2))
To make an array of arrays one must use Java's quaint array type naming conventions, see section 20.3.2, page 466, of [9].5In these examples a3 is an array of integer arrays, and a4 is an array of object arrays.

Array Access

The elements of an array are accessed via:

  
(aget <array> <nexp>)
which returns the value of <array>[<nexp>], both <array> and <nexp> are evaluated. For example, in the context of the previous array construction examples, both of these expressions
(aget a1 (int 0))                   
(aget (aget a3 (int 1)) (int 0))
evaluate to 88. Whereas
(aget a2 (int 0))                   
(aget (aget a4 (int 1)) (int 0))
both evaluate to the same instance of the java.io.PrintStream class associated with the standard error stream.

Array Assignment

Similarly an array may be set via:

(aset <array> <nexp> <expV>)
which sets <array>[<nexp>] to be the value of <expV>. It also returns the value of <expV>. All three subexpressions are evaluated from left to right. As in the case of array construction the value of the expression <expV> must be an allowable element of the array.

So to continue our running examples, we could modify our integer array, a1, by evaluating the expression

(aset a1 (int 0) (char 'Z'))
and accessing a1 would reflect this change
(aget a1 (int 0))
and evaluate to 90.

Strings

The $\hbox {\tt JLambda}$ reader will interpret any string of characters beginning and ending with the character " as the corresponding Java string (without the two occurrences of the character "), actually "foo" is interpreted as (quote foo), which evaluates to the Java String foo.

String concatenation is provided by the concatenation form:

 
(concat  <exp> .... <exp>)
This form of String concatenation, evaluates each expression and concatenates the result (using the toString() method of each object returned, and the usual conversions in the case of primitive data). So a simple example of this is that
(concat (char '\t') "A " "short " "sentence.")
evaluates to a string that prints as
        A short sentence.

Arbitrary Objects

Object Construction

Arbitrary Java objects may be constructed using the following form:

(object (<exp> <exp_1> ... <exp_N>))
where $\mathtt{N} \geq 0$. The arguments are, as usual, evaluated from left to right, the first argument <exp> should evaluate to string representing the full name of a Java class. If the so named Java class is not public an exception is thrown. The interpreter then attempts to find a constructor for that class with matching arguments, and using that constructor and the remaining arguments, constructs the appropriate object. If no matching constructor is found an exception is thrown. We describe this resolution process in more detail in section 6.

For example we can construct a generic object by evaluating

(object ("java.lang.Object"))
and a wrapper object of the character X via
(object ("java.lang.Character" (char 'X')))
A slightly more interesting example is a frame
(define frame (object ("java.awt.Frame" "A Frame")))
that we will futz with shortly.

The null object reference is obtained by the special form:

(object null)
which evaluates to null.

Field Access

Static and non-static fields of an object can be accessed via:

 (lookup <target> <field>)
The <target> and <field> positions are evaluated from left to right. The interpreter then looks for a field belonging to the object that the <target> evaluates to. The value of that field is returned. If no corresponding field is found an exception is thrown. If <target> does not evaluate to an object an exception is also thrown.

For example the following expressions both evaluate to

(lookup a1 "length")
(lookup a4 "length")
to the integer 3.6

It is, of course, the runtime type of the value of <target> that is used in determining the appropriate field, and its value. (Since this is an interpreted language, there is no sensible notion of compile time type.) Thus static fields are probably best looked up via the class rather than an instance if there is the possibility of ambiguity.

The value of a static field may also be accessed from the class via the usual notation

java.lang.Math.PI

Field Updating

In early versions of $\hbox {\tt JLambda}$ we did not provide any support for the setting of fields, either instance or static. This was not because it was problematic, but simply because we thought it would be unnecessary, well designed classes enforce object encapsulation of state. If an object's fields are supposed to be updatable, the class will provide methods to do so. Unfortunately, this was overly optimistic, some built in Java classes are no more sophisticated than C structs, for example java.awt.GrdBagConstraints. Consequently, we now provide means of setting the values of fields, either instance or static.

Static and non static fields of an object may be invoked using the following form:

(update <target> <field> <exp>)
The arguments are evaluated from left to right, and then the interpreter attempts to update the field, whose name is the value of <field> (which should be a String), to the value of the expression <exp>. The return value of the entire expression is the new value of the field.

Thus for example we could create two identical points as follows:

(define p1  (object ("java.awt.Point  (int 50) (int 50))))
(define p2  (object ("java.awt.Point)))
(update p2 "x" (int 50))
(update p2 "y" (int 50))
Alternately, these last two expressions could be abbreviated to:
(update p2 "y" (update p2 "x" (int 50)))
In the case where one wishes to update a static field via the class rather than the object, for example in the situation where there are no instances of the class, we include the following form:
(supdate <target> <field> <exp>)
In this case <target> should evaluate to a string that names the desired class. The evaluation then proceeds in a similar fashion to the non static case. For example, to enable debugging of the entry point to the Graphics 2D actor, one could execute:
(supdate "g2d.Main" "DEBUG" (boolean true))

Method Invocation

Static and non static methods of an object may be invoked using the following form:

(invoke <target> <method> <exp_1> ... <exp_N>)
where $\mathtt{N} \geq 0$. The arguments are evaluated from left to right, and then the interpreter attempts to find a method, whose name is the value of <method> (which should be a String), with the appropriate arguments. If no method is found an exception is thrown. This resolution process is described in more detail in section 6.

Thus for example we could configure and display our frame via the following sequence of invocations

(invoke frame "setSize"  (int 50) (int 50)) 
(invoke frame "setLocation" (int 10) (int 10))
(invoke frame "setVisible" (boolean true))
which would cause the frame to be a square of fifty pixels positioned in the top left hand corner of the screen.

In the case where one wishes to invoke a static method via the class rather than the object we include the following form:

(sinvoke <target> <method> <exp_1> ... <exp_N>)
where $\mathtt{N} \geq 0$. In this case <target> should evaluate to a string that names the desired class. The evaluation then proceeds in a similar fashion to the non static case.

So for example if we wished to configure and position our frame, so that it was half the size of our screen, and centered therein, we could do the following sequence of instructions.

(define toolkit (sinvoke "java.awt.Toolkit" "getDefaultToolkit"))
(define dim (invoke toolkit "getScreenSize"))
(define h (lookup dim "height"))
(define w (lookup dim "width"))
(invoke frame "setSize" (/ w (int 2)) (/ h (int 2))) 
(invoke frame "setLocation" (/ w (int 4)) (/ h (int 4))) 
(invoke frame "setVisible" (boolean true))

To give another example, the class object corresponding to java.lang.Math can be obtained, and named, via the expression

(define math (sinvoke "java.lang.Class" "forName" "java.lang.Math"))
But it would be a mistake to try and access static fields of the java.lang.Math class, say PI, by then doing
(lookup math "PI")
because this will actually end up looking for a field called "PI" in the class that math is an instance of, i.e. java.lang.Class. To access static fields via the class use:
java.lang.Math.PI

Control Forms

The usual Scheme/Lisp forms are provided.

Lexical Binding

Lexical binding is available via:

(let (
      (<var_1> <exp_1>)
      (<var_2> <exp_2>)
      ...
      (<var_N> <exp_N>)
     )
 <exp>
 )
where $\mathtt{N} \geq 1$. This expression incrementally augments the current lexical environment. I.e. the binding of <var_1> to the value of <exp_1> is visible in the evaluation of <exp_2>, and all subsequent expressions. The value of the entire expression is the value of <exp> in the fully augmented environment. So for example if one were nostalgic for the initial UNIX process file descriptor table, one could construct the following array:
(let ((0 java.lang.System.in)
      (1 java.lang.System.out)
      (2 java.lang.System.err))
  (array java.lang.Object 0 1 2))

Sequencing

Sequencing is available via the form:

(seq <exp_1> ... <exp_N>)
where $\mathtt{N} \geq 0$. The value returned from a sequencing expression is the value returned by the last expression in the sequence, or null in the case that $\mathtt{N} = 0$.

Thus a simple use of these last two forms would be to construct our centered frame, without littering the global environment with debris.

(let ((frame (object ("java.awt.Frame" "A Frame")))
      (toolkit (sinvoke "java.awt.Toolkit" "getDefaultToolkit"))
      (dim (invoke toolkit "getScreenSize"))
      (h (lookup dim "height"))
      (w (lookup dim "width")))
  (seq 
   (invoke frame "setSize" (/ w (int 2)) (/ h (int 2)))
   (invoke frame "setLocation" (/ w (int 4)) (/ h (int 4))) 
   (invoke frame "setVisible" (boolean true))
   frame)
)

Lambda Expressions and Closures

Closures are obtained by evaluating the corresponding lambda form:

(lambda (<param_1> ... <param_N>) <exp>)
where $\mathtt{N} \geq 0$, The value of such an expression is a closure, a pair consisting of the lambda expression, and the lexical environment at the time of evaluation. Thunks (the case when N is zero) are permissible. As an example of this we could define and name a centered frame factory as follows.
(define frameFactory
  (lambda (FrameName)
    (let ((frame (object ("java.awt.Frame" FrameName)))
          (toolkit (sinvoke "java.awt.Toolkit" "getDefaultToolkit"))
          (dim (invoke toolkit "getScreenSize"))
          (h (lookup dim "height"))
          (w (lookup dim "width")))
       (seq 
         (invoke frame "setSize" (/ w (int 2)) (/ h (int 2)))
         (invoke frame "setLocation" (/ w (int 4)) (/ h (int 4))) 
         (invoke frame "setVisible" (boolean true))
         frame)
    )
   )
)

Closure Application

Application of a closure to the appropriate number of arguments is via the apply form:

(apply <exp> <exp_1> ... <exp_N>)
where $\mathtt{N} \geq 0$. To litter our screen with annnoying popups we could then do
(apply frameFactory "Viagra popup")
(apply frameFactory "Nigerian bank scam")
(apply frameFactory "Lottery notification")
(apply frameFactory "Random porn site here!")
and save other people the time and effort.

Iteration

An iteration form is included based on the Scheme do form [10].

(do  (
      (<var_1> <init_1> <step_1>)
      ... 
      (<var_N> <init_N> <step_N>)
     ) 
     (<test> <exp> ...)
     <command> ...)
where $\mathtt{N} \geq 1$, Evaluation of a do form proceeds in two steps, an initialization phase followed by an iteration phase. In the initialization phase the <init_i> expressions are evaluated sequentially in the outer environment. Their values are then bound to the variables <init_i> to form the do environment, and the iteration phase commences. In each iteration the following takes place. The <test> expression is evaluated, if it does not evaluate to a boolean an exception is thrown. If it evaluates to true, then the <exp> ... are evaluated in order, and the value of the last expression is the value of the entire do form. Otherwise each of the <command> ... expressions are evaluated for their effect, then the variables in the do environment are updated, sequentially, to be the values of the <step_i> forms, and the next iteration commences.

So a simple example is

(do (
     (i (int 0) (+ i (int 1)))
     (str " " (concat str " " i))
    )
    ((= i (int 10))  str)
    (invoke java.lang.System.err "println" str)
    )
)
prints out a triangle of numbers:
  1
  1 2
  1 2 3
  1 2 3 4
  1 2 3 4 5
  1 2 3 4 5 6
  1 2 3 4 5 6 7
  1 2 3 4 5 6 7 8
  1 2 3 4 5 6 7 8 9

In Java 1.5 a for-each construct was introduced, which enables the programmer to conveniently iterate over a collection or array. $\hbox {\tt JLambda}$ supports this construct via the for form:

 
(for <var> <exp> <body>)

Here, <exp> should evaluate to either an array object or a java.util.Collection instance; otherwise an exception is raised. <body> is evaluated once for each element of the array/collection, in an environment in which <var> is bound to the current element. The value of the entire for form is the value returned by the final evaluation of <body>.

In this simple example,

(for e
     (array int (int 3) (int 5) (int 7))
     (invoke java.lang.System.err "println" e))

prints the list of numbers:

3
5
7

Branching

Both binary and ternary forms of the branching primitive are allowed:

 
(if <test> <then>) 
(if <test> <then> <else>)
In both the binary and ternary forms the <test> should return a boolean, The binary form returns null if <test> is false.

Boolean Operations

The related propositional forms are as usual:

(and <exp> .... <exp>)
(or <exp> .... <exp>)
(not <exp>)
The expressions are evaluated from left to right, their values should be boolean, otherwise an exception is thrown. In the case of and, evaluation is terminated on the first false value, while in the case of or, evaluation is terminated upon the first true value encountered.

Quoting

Finally evaluation can be prevented via the usual quote form:

 
(quote <exp>)
which returns the unevaluated <exp> as data, either a String or a List.

Subtyping

There is a version of Java's instanceof operator. The expression:

(instanceof <exp> <exp>)
returns Java's idea of whether or not the value of the first <exp> is an instance of the class named by the (String) value of the second <exp>. For example, the expression
(instanceof "java.lang.String" "java.lang.String")
will evaluate to true. Note that there is a clear distinction made between primitive data and the associated wrapped form, an expression such as
 (instanceof (int 7) "java.lang.Integer")
will evaluate to false.

File loading and single line comments

The read-eval-print loop interface to the $\hbox {\tt JLambda}$ language is rudimentary, reading and evaluating a single line at a time. Consequently, loading a file containing a single expression using the load expression is useful:

(load <exp>)
This loads in the contents of the file, parsed as a single S-expression. The expression <exp> should evaluate to a string naming the file.

In the file loaded comments may appear. The $\hbox {\tt JLambda}$ language only has single line comments. The comment character is the semicolon ;, with the proviso that it is preceeded by whitespace, or begins a new line.

Class Names, Inner Classes, and Enums

All of Java's classes may be accessed by their full names. There is no import mechanism, other than that provided by the lexical and dynamic binding primitives:

(define Vector "java.util.Vector")

(let ((Line "java.awt.geom.Line2D$Double")
      (victor (object (Line (int 0) (int 0) length (int 0))))
  (object (Vector victor)))
Note that, following Java's reflection conventions, inner classes are accessed via the $ rather than the .. So for example the elements of PointStyle enum in g2d.plot.PlotDescription would be accessed by
g2d.plot.PlotDescription$PointStyle.CIRCLE

Exceptions

One can throw an exception using the throw form:

(throw <exp>)
The expression <exp> should evaluate to an instance of java.lang.Throwable, otherwise a somewhat different exception will be thrown. There is also a try catch form for handling any exceptions that may be generated. It is closely related to the Java form, without the option of multiple catches, or a finally clause.
(try <exp_0>  (catch <var> <exp_1>))
Evaluation of this form proceeds by first evaluating <exp_0>, if this succeeds without generating an exception, then the value of the entire expression is the value returned by <exp_0>. However, if evaluation of <exp_0> generates an exception, then this exception is bound to the variable <var>, and <exp_1> is evaluated in this larger environment, and its value is the value of the entire try catch expression. For example,
(try (throw (object ("java.lang.Throwable"))) 
     (catch var (boolean true)))
(try (boolean false) 
     (catch var (boolean true)))
the first expression evaluates to the boolean true, while the second evaluates to the boolean false.


The Glyphish Hierarchy

A description of the $\hbox {\tt JLambda}$ language would not be complete without touching on the Glyphish hierarchy mentioned in section 1, and in particular it's use of the $\hbox {\tt JLambda}$ language. This class hierarchy belongs to the g2d.glyph package, whose class structure is shown below:

\pstree[levelsep=10mm,nodesep=3pt]{\TR{\tt\small Identifiable}}{%
\pstree[level...
...TR{\tt\small Glyph}
\TR{\small\tt GlyphList}
\TR{\small\tt ClosureGlyph}
}}}
Javadoc documentation for these and other classes can be found at [11].

The Identifiable Class

Graphical objects that are manipulated by other actors (for example formal reasoning tools such as Maude and PVS) may be assigned unique identifiers by them. Unique identifiers are strings. To access an object by it's unique identifier the fetch form is used:

(fetch <exp>)
An object, belonging to a class extending Identifiable, may be assigned a unique identifier using the setUID method. It is an error, i.e. an exception will be thrown, to assign the same identifier to two objects, and it is also an error to assign an identifier to an object that has already been assigned an identifier. Similarly one accesses an particular objects unique identifier by the getUID method. If the instance of the Identifiable class has not been assigned a unique identifier the getUID method returns null.

The Attributable Class

An object belonging to a class extending Attributable may have new fields added dynamically, in the form of attributes. An attribute has a name and a value. For efficiency attributes are manipulated via the dedicated forms:

(setAttr <exp> <exp> <exp>) 
(getAttr <exp> <exp> [<default>])

rather than by the corresponding methods of the object involved. The getAttr form has an optional default argument. This is the value returned, in the case where the object has no attribute by the specified name. If no default is specified, and no attribute is found, then null is returned. In both forms the first expression evaluates to the target instance, the second evaluates to the name of the attribute (which should be a string), while in the setAttr form the third evaluates to the new desired value, while in the getAttrform the optional third expression evaluates to the default value to be returned, in the case where the object has no attribute by that name.

Attribute values are arbitrary objects, including closures. An attribute with a closure value corresponds to a dynamic method. Two ways to make use of such a dynamic method is with an apply expression, or by invoking one of the Closure's many applyClosure convenience methods directly on the attribute.

  (setAttr att "method" (lambda (x) ...))
  ...
  (apply (getAttr att  "method") (int 7))
  ...
  (invoke (getAttr att "method") "applyClosure" (object null))

In section 4 we present in detail an example that, amongst other things, uses attributes to implement mouse draggable graphical objects.

Glyphs

In Joel Bartlett's Ezd package a Glyph was something that knew how to draw itself, typically as a sequence of shapes, and was capable of accepting and responding to user inputs. The Ezd package was developed using the early Java 1.0 event model, and relied on the java.awt package as it was then, in Java 1.0.

We adopt a similar, though distinct, conceptual approach. Our approach has been strongly influenced by the newer Java event model, and the clean two dimensional graphics supplied by the Java 2D API. In particular we make heavy use of the AffineTransform class of the geom subpackage of java.awt, and the newer 2D implementations of the Shape interface, also of the java.awt package. We have also taken advantage of the Closure class provided by the $\hbox {\tt JLambda}$ language.

In our approach the root class of all things glyph-like is the abstract class Glyphish. There are three related but distinct aspects to the Glyphish class:

  1. How a Glyphish instance depicts or portrays itself graphically, typically this is done at the request of an encompassing Component.

  2. How a Glyphish instance handles input events from the keyboard and mouse, again typically delegated by an encompassing Component.

  3. How a Glyphish instance positions or transforms itself within it's encompassing Component.

The abstract Glyphish class has three main immediate concrete subclasses:

The Glyph class is the simplest of the direct subclasses of Glyphish. A Glyph instance has a single java.awt.Shape, border colour, fill colour, and stroke width. A GlyphList is a composite, it consists of an ordered list of Glyphish things. A ClosureGlyph is the most dynamic, it requires $\hbox {\tt JLambda}$ closures to implement all the abstract methods required by the Glyphish API. It provides, in essence, a way of defining, at runtime, Glyphish instances whose methods are defined at runtime, rather than compile time.

Depiction

A concrete Glyphish instance portrays itself by implementing the abstract method

void paint(java.awt.Graphics2D g2d);
declared in the Glyphish class. In the case of a Glyph instance it will draw itself according to its java.awt.Shape field, fill colour, border colour and stroke. In the case of a GlyphList it merely delegates, in order, to all of the Glyphish elements in its list. The ClosureGlyph responds by applying it's private
Closure paintClosure;
field to the appropriate Graphics2D object of the java.awt package.

Events

Glyphish instances are capable of handling any input events, i.e. instances of the InputEvent class of the java.awt.event package. To determine whether a Glyphish instance is the desired target of an Input event the abstract method

boolean inside(Point2D p);
of the Glyphish class is used, here the class Point2D belongs to the java.awt.geom package. The Glyph class implements this by using it's java.awt.Shape's field corresponding
boolean contains(Point2D p);
method. The GlyphList instance implements this by iterating through it's list of Glyphish elements calling each element's inside method, and returns true if one returns true, else it returns false. The ClosureGlyph responds by applying it's private
Closure insideClosure;
field to the appropriate Point2D object.

The Glyphish class implements each of the Input event listener interfaces: MouseListener, MouseMotionListener, and KeyListener, all of the package java.awt.event. They do so in a uniform way. For each method in the listener interface a Glyphish instance has a Closure object associated with it. For example in the case of the

void mouseClicked(MouseEvent e);
method of the MouseListener class, the Glyphish class has the private field
Closure  mouseClickedAction;
This closure will have arity 2, and uses Luca Cardelli's trick of having a self argument to implement Java's this pointer. Calling the
mouseClicked(MouseEvent e)
method would result in the clickedAction closure being applied:
clickedAction.applyClosure(this, e);
where the this pointer is of the Glyphish instance that is responding to the MouseEvent instance e.

Transformation

Positioning, moving, and animating Glyphish instances is done by applying affine transformations (e.g. translating, rotating, shearing, and scaling) to them.

A concrete instance of the Glyphish class transforms itself by providing an implementation to the abstract method

void transform(AffineTransform a);
of the Glyphish class. In the case of a Glyph instance, the AffineTransform a is applied to the instance's Shape. In the case of the GlyphList instance, it is applied to each Glyphish element of its list. While the ClosureGlyph simply applies it's private
Closure transformClosure;
to the AffineTransform object a.

Closures

Another use of the Closure class provided by the $\hbox {\tt JLambda}$ language is in creating event handlers. For each Java listener class, we provide a corresponding template class that implements the desired listener interface, and extends the Attributable class. An instance of the template class can be specified by providing Closures for each of the required methods. As mentioned above, we have adopted the convention that these closures take two arguments, the self parameter, followed by the event parameter.

A simple example of this, corresponding to Java's ActionListener interface of the java.awt.event package, is the ClosureActionListener class of the g2d.closure package. The action listener interface requires the solitary

void actionPerformed(ActionEvent e)
to be implemented. Consequently the ClosureActionListener class has a private field
Closure actionPerformedClosure;
a setter for this field:
public void setActionClosure(Closure closure){
    if(closure.getArity() == 2) {
            actionPerformedClosure = closure;
    } else { // report an error }
}
The required actionPerformed method is then simply implemented by:
public void actionPerformed(ActionEvent e){
   if(actionPerformedClosure != null) { 
     actionPerformedClosure.applyClosure(this, e); 
   }
}


A Complete JLambda Program

In this section we present a complete example of a JLambda program. The program, clicker, contains the most interesting features of JLambda, namely, the creation and manipulation of Java objects, and the use of closures [12] as event handlers.

The clicker program, a full listing of which can be found at [13], constructs graphical objects using the g2d class hierarchy [11]. When the clicker program is run, it displays a window containing a single circular node. Clicking anywhere in the window creates a new node at that point. The following is a screenshot of the clicker program window showing a single node.

\includegraphics[scale=0.5]{clicker1.eps}

The user can change the colour of an existing node by shift-clicking on it; a dialogue box will appear, allowing the user to select a new colour for the node. The following is a screenshot of the clicker program window showing several nodes, some of which have had their colour changed by the user.

\includegraphics[scale=0.5]{clicker2.eps}

Finally, existing nodes can be dragged using the mouse.

In Section 4.1 we present an overview of the lexical structure of the clicker program code, in an attempt to orient the subsequent discussion. Section 4.2 contains details each section of the clicker program code, along with an explanation of its operation.


The clicker's Lexical Structure

To aid navigation of the clicker source code, we provide the following representation of its lexical structure.

(let ((h ...)
      (w ...)
      (black ...)
      (yellow ...)
      (stroke ...)
      (ellipse ...)
      (view ...)
      (frame ...)
      (mkNode
       (lambda (...)
         (let ((node ...)
               (pressed ...)
               (released ...)
               (dragged ...)
               (clicked ...)
               (trans ...))
           (seq ... (invoke ...) ...))))
      (clickedV ...))
  (seq ...))


Code Listing for clicker

The clicker program begins by creating the main window object; it defines bindings for some Java primitive types, fields, and objects, which are used to specify the initial properties of the node object, and the g2d.swing.IOPFrame object, which is the program's main window.

(let ((h (int 50))
      (w (int 70))
      (black java.awt.Color.black)
      (yellow java.awt.Color.yellow)
      (stroke (object ("java.awt.BasicStroke" (float 2.5))))
      (ellipse (object ("java.awt.geom.Ellipse2D$Double"
                        (int 0) (int 0) w  h)))
      (view (object ("g2d.swing.IOPView"
                     (boolean true) (boolean true))))
      (frame (object ("g2d.swing.IOPFrame" "Node Example" view)))

Note that the use of these bindings relies on a particular semantics of JLambda's let expression: that each binding incrementally augments the lexical environment.

The next code section defines a closure which, when invoked, constructs a node object.

(mkNode
 (lambda (xPos yPos)
   (let ((node (let ((temp (object ("g2d.glyph.Glyph"
                                    ellipse black yellow))))
                 (seq (invoke temp "setStroke" stroke)
                      temp)))

In creating the node, the closure uses several bindings from the previous section to specify initial properties, such as colour, of the node.

The mkNode closure also creates several closures for each of the mouse click events to which the node responds, and binds them to the variables pressed, released, dragged, and clicked. Later in the program, these closures will be registered as event handlers for the newly-created node.

The next section of the program contains the definition of each of these closures:

(pressed (lambda (self event)
           (seq
            (setAttr self
                     "pointF"
                     (object ("java.awt.geom.Point2D$Double"
                              (invoke event "getX")
                              (invoke event "getY"))))
            (setAttr self "draggedF" (boolean true)))))

(released (lambda (self event)
            (setAttr self "draggedF" (boolean false))))

(dragged 
 (lambda (self event)
   (let ((dragon (getAttr self "draggedF")))
     (if (and (!= dragon (object null)) dragon) 
         (let ((pnt (getAttr self "pointF"))
               (eX (invoke event "getX"))
               (eY (invoke event "getY"))
               (a (let ((temp (object ("java.awt.geom.AffineTransform"))))
                    (seq (invoke temp 
                                 "translate" 
                                 (- eX (invoke pnt "getX"))
                                 (- eY (invoke pnt "getY")))
                         temp))))
           (seq (invoke self "transform" a)
                (invoke pnt "setLocation" eX eY)
                (invoke view "repaint")))))))

(clicked (lambda (self event)
           (if (invoke event "isShiftDown")
               (let ((chooser (object
                               ("javax.swing.JColorChooser")))
                     (color (invoke chooser
                                    "showDialog"
                                    frame
                                    "Color Chooser"
                                    (invoke self "getFill"))))
                 (seq (if (!= color (object null))
                          (invoke self "setFill" color))
                      (invoke view "repaint"))))))

When an event handler closure is invoked it receives two arguments: the object receiving the event, and the event object itself. The closure uses information stored in the event object to manipulate the receiving object.

Each of the above closures manipulate the node object in ways that correspond to the type of event the node object received. For example, the clicked closure checks whether the shift key is depressed; if the key is depressed, the closure displays a dialogue box, and changes the colour of the node to the selected colour.

Next, a java.awt.geom.AffineTransform object is defined, which will later be used to provide translation of the node object in response to a mouse drag event.

(trans (let ((temp (object ("java.awt.geom.AffineTransform"))))
         (seq (invoke temp "translate" xPos yPos)
              temp))))

The event handling closures that have been created and bound to variables must now be registered as event handlers for the node object. The following code sequence invokes the setMouseAction method of the node object to register closures for each of the different types of mouse event to which the node responds.

(seq
 (invoke node
         "setMouseAction"
         java.awt.event.MouseEvent.MOUSE_PRESSED
         pressed)
 (invoke node
         "setMouseAction"
         java.awt.event.MouseEvent.MOUSE_RELEASED
         released)
 (invoke node
         "setMouseAction"
         java.awt.event.MouseEvent.MOUSE_CLICKED
         clicked)
 (invoke node
         "setMouseAction"
         java.awt.event.MouseEvent.MOUSE_DRAGGED
         dragged)
 (invoke view "add" node trans)))))

The final invoke expression in the preceding code sequence simply adds the translated node to the view object.

The program now defines a closure that creates a new node in response to a mouse click event.

(clickedV (lambda (self event)
            (if (not (invoke event "isShiftDown"))
                (seq
                 (apply mkNode
                        (- (invoke event "getX")
                           (/ w (int 2)))
                        (- (invoke event "getY")
                           (/ h (int 2))))
                 (invoke view "repaint"))))))

The clickedV closure receives the window object and event object as arguments, and uses the event object to determine the position at which to create the new node. clickedV creates a new node object by calling the mkNode closure defined previously.

Since a mouse click event creates a new node, it must be received by the window object, and not a node object. Consequently, the closure that handles mouse click events is not created within the body of mkNode, unlike the previously defined event handler closures.

The remaining section of code simply registers the node-creation event handler, clickedV, with the window object, and displays an initial node.

(seq
 (invoke view
         "setMouseAction"
         java.awt.event.MouseEvent.MOUSE_CLICKED
         clickedV)
 (apply mkNode (* w (int 3)) (* h (int 3)))
 (invoke view "repaint")))

After the program has been evaluated the interpreter exits, and Java's AWT thread continues running to listen for window events. Closures that are invoked in response to events are interpreted in this second thread.

The code listing for the clicker program demonstrates the power and simplicity of the interface provided the JLambda language to Java's language facilities. Features such as closures and lexical scoping make construction of event-driven graphical programs, such as clicker, particularly straight-forward.


Related Work & Related Issues

In this section we describe briefly several projects related to JLambda. Additionally, we present an overview of the implementation of the JLambda interpreter.


Projects Related to JLambda

There exist several Scheme interpreters that use Java as their implementation language. Four well-known examples are SISC [14], Kawa [15], and JScheme [16], and Skij [17]. Although all of these projects aim to produce a Java-based implementation of the Scheme language, they each have unique characteristics. We present here a brief description of each project and its interface to the Java language. This is not an exhaustive list by any means, there are close to two hundred languages (200) cited in [18] that use Java or the Java Virtual Machine as a basis, roughly twenty (20) of these are classed as Scheme or Lisp like.

SISC

SISC is a Scheme interpreter implemented in Java whose primary goal is rapid execution of the complete Revised$^{5}$ Report on the Algorithmic Language Scheme [10] definition. In particular, SISC supports proper tail recursion and unrestricted first-class continuations. SISC's foremost concern is performance, and it outperforms all existing Java-based Scheme interpreters, often by more than an order of magnitude.

The interface to the Java language provided by SISC is the S2J module. Importing this module allows one to instantiate Java classes, call methods on Java objects, and access/modify fields of Java objects.

Java classes are types in SISC's extensible type system; the procedure

(java-class symbol)

returns the Java class of name symbol. Java classes can be instantiated with a call to the java-new procedure:

(java-new class args ...)

which selects a constructor of class based on the types of args and calls it, returning the newly created object.

Java fields are made accessible to Scheme code as procedures that can get/set any field of a given name on any Java object. Static Java fields can be accessed/modified by passing an instance of the appropriate class as the first argument to the procedures. The procedures

(generic-java-field-accessor symbol)
(generic-java-field-modifier symbol)
return a procedure that when invoked with a Java object as the first argument, retrieves or sets the value of the Java field named symbol in the Java object.

Methods are made accessible to Scheme code as procedures that can invoke any method of a given name on any Java object. For example, the procedure

(generic-java-method symbol)

returns a procedure that when invoked with a Java object as the first argument and Java values as the remaining arguments, invokes the best matching method named symbol on the Java object, and returns the result. Static Java methods can be invoked by passing an instance of the appropriate class as the first argument to the procedure.

Kawa

The goal of Kawa is a Scheme environment implemented in Java that compiles Scheme code into the byte-code instructions of the Java Virtual Machine. All of the required and optional features of the R5RS Scheme standard are supported, with the exception of proper tail recursion and unrestricted continuations. Kawa also provides mechanisms for the definition, creation, and access of Java objects.

In Kawa, objects are created with the procedure

(make type arg ...)

which constructs a new object instance of the specified type, which should be of the form <java.lang.Class>.

For accessing the fields of Java objects and static fields, Kawa provides two procedures, field and static-field, which return the value of the field. Note that the field must be declared public.

(field object fieldname)
(static-field class fieldname)

Fields may be set by using the field and static-field procedures as the first operand to set!. Here is an example:

(set! (field a 'car) (field b 'car))

Java instance methods and static methods may be invoked with the invoke and invoke-static procedures:

(invoke object name arg ...)
(invoke-static class name arg ...)

JScheme

The JScheme project is a dialect of Scheme whose chief feature is a simple and comprehensive interface to Java. JScheme supports all features of the R4RS standard except unrestricted first-class continuations and mutable strings. The interface to the Java language provided by JScheme is called the Javadot notation, which enables access by name to all methods, constructors, and fields of any Java class. Table 1 provides some examples of this notation.


Table 1: Examples of JScheme's Javadot notation
Syntax Type of Member Example
``.'' at end constructor (Font. NAME STYLE SIZE)
``.'' at beginning instance method (.setFont COMP FONT)
``.'' at beginning, $ at end instance field (.first$ '(1 2))
``.class'' suffix Java class Font.class
``.'' only in the middle static method (Math.round 123.458)
``$'' at end static field Font.BOLD$


Skij

Skij, developed at IBM Watson Labs by Michael Travers, is a Scheme interpreter implemented in Java designed for exploratory programming in the Java environment. The project has now been retired by IBM, and consequently is no longer available. We mention it here because its purpose is similar to that of JLambda, and several of its ideas are closely related to ideas present in JLambda.

In Skij, Java objects are created and manipulated using the primitive procedures new, invoke, peek, poke, invoke-static, peek-static, and poke-static. Class arguments must be either the fully qualified class name as a string or symbol, or a java.lang.Class object. Method and field names should be either strings or symbols.

An object is created with the new procedure:

(new class args ...)

Instance fields and static fields are accessed with the peek and peek-static procedures:

(peek object field-name)
(peek-static class field-name)

Instance fields and static fields are modified with the poke and poke-static procedures:

(poke object field-name new-value)
(poke-static class field-name new-value)

Instance methods and static methods may be invoked with the invoke and invoke-static procedures:

(invoke object method-name args ...)
(invoke-static class method-name args ...)


Overview of the JLambda Interpreter

We provide here a brief overview of the the implementation of the JLambda interpreter; further details of the interpreter's design and implementation can be found in David Porter's Honours thesis [19].

The interpreter for the JLambda language is implemented in Java, and consists of three components: a parser, a syntax analysis phase, and an evaluation phase. Choosing Java as the interpreter's implementation language leads to certain complications in the interpreter's design. In general, the simplest way to implement an interpreter of a Scheme-like language is by using a simple recursive evaluation model in which an expression is evaluated by recursively evaluating each subexpression. If such an interpreter is implemented in Java it will exhibit recursive execution behaviour, since it inherits the control structure of the underlying Java system. However, the lack of proper tail recursion in Java means the interpreter overflows the JVM stack when attempting to evaluate arbitrarily long recursions, such as the computation of long lists.

The goal then was to design the JLambda interpreter so that it uses an iterative execution process; this was achieved by implementing the interpreter as a register-machine interpreter. In this design, the procedure-calling and argument-passing mechanisms used in the evaluation process are implemented in terms of operations on registers. We thus obtained an explicit-control interpreter that exhibits iterative execution behaviour.

Converting the interpreter design from a simple recursive evaluation model to a register machine interpreter involved two steps. First, we ensured all recursive calls were tail calls, by transforming the interpreter into continuation passing style [20]. If the interpreter implementation language was properly tail recursive, this transformation would have been sufficient to achieve an interpreter with iterative execution behaviour, since properly tail recursive languages guarantee that tail recursion is equivalent to iteration. However, since we chose Java as the implementation language--which is not properly tail recursive--a further step was required, in which we manually transformed tail recursion into iteration.

The second step consisted of the transformation of the interpreter from a continuation passing style into a register-based imperative style. This transformation is based on the following observation: if a set of methods call each other only by tail calls, we can first rewrite the calls to use variable assignment instead of argument-passing, and we can then replace method calls with jumps. The register machine transformation consists of systematically performing such rewrites. The result of performing the continuation passing transformation and the register machine transformation was an interpreter with iterative execution behaviour. This interpreter can therefore properly evaluate any JLambda expression. The transformation from recursive to iterative interpreter is a standard technique in the programming language community. An example worked out in detail can be found in Felleisen's thesis [21].

The syntax analysis phase serves to improve the interpreter's execution speed. In this phase all lexical variables in a JLambda program are replaced by their corresponding lexical addresses in the program structure. Such a program representation enables the evaluator to retrieve variable values directly from known addresses in the run-time environment. Implementing variable lookup operations in this way is a considerable improvement over lookup operations based on searching the environment. Consequently, the addition to the interpreter of a syntax analysis phase yields a significant increase in its execution speed.


Method and Constructor Resolution

The Java Reflection API is both quirky, and low level. It provides objects that represent meta-level concepts such as classes, interfaces, fields, methods, constructors, and class member modifiers. It also allows for: the ability to access or update the value of an objects field; to invoke an object's method on a given set of values, or construct a new object via calling a constructor on a given set of values. What it does not provide is any means of resolving what method or constructor one should use, given a certain sequence of arguments. Consequently, it is left to the $\hbox {\tt JLambda}$ interpreter to decide on how this should be done. In Java, method and constructor resolution uses both runtime, and static compile time information. The runtime type of the target object is used, together with the compile time types of the arguments. In an interpreted language we have no static type information to rely on, and consequently we must attempt to do the best we can with the possibly incomplete information (due to the presence of the null object reference, amongst other things) we have at hand. Namely, the runtime types of the arguments.

Compared to other implementations of interpreted languages that use Java's Reflection API, see section 5, we have adopted a rather conservative approach. This is because our aim is to provide a simple faithful and precise interface to Java as is possible in an untyped interpreted language. A rather bolder scheme is outlined by Michael Travers in [22,23] and used in his language Skij [17], as well as Peter Norvig's JScheme [16].

Constructor Resolution

Evaluation of a constructor call

(object (<exp> <exp_1> ... <exp_N>))
proceeds as follows. First the arguments are evaluated from left to right, the first argument <exp> should evaluate to string representing the full name of a Java class. If the so named Java class is not public an exception is thrown. It is, of course, the runtime types of the values of the arguments <exp_1> ... <exp_N> that are used to determine the appropriate constructor. If an argument's value is null it's type is treated like a wild card object reference.

N.B. Only constructors that are declared public qualify in this search.

The search proceeds as follows. First the interpreter looks for a constructor that exactly matches the argument types (using the

public Constructor getConstructor(Class[] parameterTypes)
method of the java.lang.Class class). Otherwise we look among the public constructors for the best match, the candidates are chosen from those returned by the
public Constructor[] getConstructors()
method of the java.lang.Class class, not, for example, the
public Constructor[] getDeclaredConstructors()
method of the java.lang.Class class). The notion of best is taken to mean the least when taking widening, the interface hierarchy, and the class hierarchy into consideration. There may be many such choices, and presently we simply choose one, making no effort to resolve ambiguities. If no constructor is found an exception is thrown.

Method Resolution

Evaluation of a static or non static method call following form:

(invoke <target> <method> <exp_1> ... <exp_N>)
proceeds as follows. The arguments are evaluated from left to right, and then the interpreter attempts to find a method, whose name is the value of <method> (which should be a String), with the appropriate arguments. It is the method of the class that the runtime value of <target> belongs to, that is subsequently found and invoked.

If no matching method is found an exception is thrown. The search is almost identical to the one undertaken in the object construction case. It is, of course, the runtime types of the values of the arguments <exp_1> ... <exp_N> that are used to determine the appropriate method. As in the constructor case, if an argument's value is null it's type is treated like a wild card object reference.

N.B. Only methods that are declared public qualify in this search.

First the interpreter looks for a method that exactly matches the argument types (using the

public Method getMethod(String name, Class[] parameterTypes)
method of the java.lang.Class class). Otherwise we look among the public methods for the best match, the candidates are chosen from those returned by the
public Method[] getMethods()
method of the java.lang.Class class, not, for example, the
public Method[] getDeclaredMethods()
method of the java.lang.Class class). The notion of best is elaborated slightly from the constructor case to take into account the declared return types of the method. This is only used when the parameter types match exactly, and in this case the more specific return type is preferred. (See sections 8.2 and 8.4 of [9], and the API for the
public Method getMethod(String name, Class[] parameterTypes)
method of the java.lang.Class class). If no method is found an exception is thrown.

The reader should be warned that there is a long standing unresolved bug in the Java Reflection API [24] that has to do with access restrictions on inner class objects. Some methods, that should be accessible, are not. For example instances of java.util.Iterator that are returned by java.util.Collection instances are often unusable. We attempt to circumvent this problem by suppressing the Java language access checking, using the setAccessible method of the java.lang.reflect.AccessibleObject class, when situations like this arise.

Variations on this Theme

The main differences between the resolution process we have described here, and the one suggested by Travers, apart from memoisation, is that we attempt to obey Java's access restrictions, and restrict our attention to public methods and constructors. Travers on the other hand considers all possible methods, and uses the Java 1.2's class java.lang.reflect.AccessibleObject to suppress access checking. We, on the other hand, only resort to this to circumvent Java's Reflection API idiosyncrasies.

Bibliography

1
M. Clavel, F. Durán, S. Eker, P. Lincoln, N. Marti-Oliet, J. Meseguer, and C. Talcott.
Maude 2.0 Manual, 2003.
http://maude.csl.sri.com/maude2-manual.

2
J. Crow, S. Owre, J. Rushby, N. Shankar, and M. Srivas.
A Tutorial Introduction to PVS.
Technical report, SRI International, 1995.
Presented at WIFT '95: Workshop on Industrial-Strength Formal Specification Techniques, Boca Raton, Florida.

3
Saddek Bensalem, Vijay Ganesh, Yassine Lakhnech, César Mu noz, Sam Owre, Harald Rueß, John Rushby, Vlad Rusu, Hassen Saïdi, N. Shankar, Eli Singerman, and Ashish Tiwari.
An overview of SAL.
In C. Michael Holloway, editor, LFM 2000: Fifth NASA Langley Formal Methods Workshop, pages 187-196, Hampton, VA, jun 2000. NASA Langley Research Center.

4
I. A. Mason and C. L. Talcott.
IOP: The InterOperability Platform & IMaude: An Interactive Extension of Maude.
In International Workshop on Rewriting Logic and its Applications (WRLA 2004), Electronic Notes in Theoretical Computer Science. Elsevier Science, 2004.

5
Ben Funnell.
The Glyphics Hierarchy., 2004.
http://mcs.une.edu.au/~iop/Data/Papers/.

6
Joel Bartlett.
Ezd - easy-to-use structured graphics for Java.
http://research.compaq.com/wrl/projects/Ezd/home.html.

7
http://java.sun.com/products/java-media/2D/forDevelopers/java2dfaq.html.
The Java 2D FAQ.

8
http://mcs.une.edu.au/~iop.
The IOP Homepage.

9
James Gosling, Bill Joy, and Guy Steele.
Java Language Specification: The Java Series.
Addison-Wesley Longman Publishing Co., Inc., 1999.

10
Richard Kelsey, William Clinger, and Jonathan Rees (Editors).
Revised$^{5}$ report on the algorithmic language Scheme.
ACM SIGPLAN Notices, 33(9):26-76, 1998.

11
http://mcs.une.edu.au/~iop/GraphicsActor2D/doc/.
The Graphics2D Actor API.

12
Alan Bawden and Jonathan Rees.
Syntactic Closures.
In Proceedings of the 1988 ACM Symposium of LISP and Functional Programming, pages 86-95. ACM Press, 1988.

13
http://mcs.une.edu.au/~iop/Data/JLambda/Misc/clicker.lsp.
The Code Listing of the Clicker Example.

14
Scott G. Miller.
SISC: A Complete Scheme Interpreter in Java.
Technical report, Indiana University, January 2002.

15
Per Bothner.
Kawa - Compiling Dynamic Languages to the Java VM.
In Proceedings of the Usenix Annual Technical Conference, June 1998.

16
K. Anderson, T. Hickey, and P. Norvig.
SILK: A Playful Blend of Scheme and Java.
In Proceedings of the Workshop on Scheme and Functional Programming, pages 13-22, September 2000.

17
http://xenia.media.mit.edu/~mt/skij/index.html.
Skij Homepage, 2004.

18
http://www.robert-tolksdorf.de/vmlanguages.html.
Programming Languages for the Java Virtual Machine.

19
David Porter.
An Interpreter for JLambda., 2004.
http://mcs.une.edu.au/~iop/Data/Papers/.

20
G. Plotkin.
Call by Name, Call by Value, and the Lambda Calculus.
Theoretical Computer Science, 1, 1974.

21
M. Felleisen.
The Calculi of Lambda-v-cs Conversion: A Syntactic Theory of Control and State in Imperative Higher-Order Programming Languages.
PhD thesis, Indiana University, 1987.

22
Michael Travers.
What is interactive scripting?
Dr Dobb's Journal, 25:103-110, 2000.

23
Michael Travers.
Scripting and dynamic interaction in Java.
Online at http://xenia.media.mit.edu/~mt/skij/dynjava/dynjava.html.

24
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957.
The Java Reflection API Bug.

About this document ...

The $\hbox {\tt JLambda}$ Language
version 0311

This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.70)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -split +0 jlambda.tex

The translation was initiated by InterOperabilty Platform on 2008-10-10


Footnotes

... 0311
We would like to thank Ben Funnell and Linda Briesemeister for suggesting improvements to this paper.
... Mason2
University of New England, Armidale, Australia, 2350. iam@turing.une.edu.au
... Porter3
University of New England, Armidale, Australia, 2350. dporter@turing.une.edu.au
... Talcott4
SRI International, Menlo Park, California, USA, 94025. clt@csl.sri.com
...java-spec.5
Java's internal name for an array class consists of the name of the element type ( B for byte, C for char, D for double, F for float, I for int, J for long, S for short, Z for boolean, and Lclassname; for classes or interfaces ) preceeded by one or more [ characters, depending on the depth of array nesting. Thus [[[[Z would be the name for an array of arrays of arrays of arrays of booleans.
... 3.6
That these evaluate correctly is due to an ad hoc clause in our interpreter, since from the point of view of Java's reflection API, length is not a field of an array, contradicting the Java language specification, see section 10.7, page 197, of [9]

next_inactive up previous
InterOperabilty Platform 2008-10-10