The Kawa Scheme Language

The Kawa Scheme Language

Miscellaneous topics

scheme-implementation-version

Returns the Kawa version number as a string.

scheme-window [shared]

Create a read-eval-print-loop in a new top-level window. If shared is true, it uses the same environment as the current (interaction-environment); if not (the default), a new top-level environment is created.

You can create multiple top-level window that can co-exist. They run in separate threads.

Building JavaFX applications

Kawa makes it easy to build “rich client” (i.e. GUI) applications using JavaFX. For example the following program will print up a window with a button; clicking on the button will print a message on the console output about the event.

(require 'javafx-defs)
(javafx-application)

(javafx-scene
 title: "Hello Button"
 width: 600 height: 450
 (Button
  text: "Click Me"
  layout-x: 25
  layout-y: 40
  on-action: (lambda (e) (format #t "Event: ~s~%~!" e))))

JavaFX support is builtin to the pre-built kawa-1.14.1.jar. It is easiest to use JDK 8; see below if you're using JDK 7. If you build Kawa from source, specify --with-javafx on the configure command line (assuming you're using JDK 8).

Assume the above file is HelloButton1.scm, you can run it like this:

$ kawa HelloButton1.scm

For more information and examples read this (slightly older) introduction, and this on animation.

Using JavaFX with JDK 7

JDK 8 ships with JavaFX, and it is in the default CLASSPATH. JDK 7 update 9 or later does have JavaFX included, but it is a separate jfxrt.jar which not in the default CLASSPATH. Thus have have to eplicitly add jfxrt.jar. To run the previous HelloButton1.scm you can do:

java -cp $JAVA_HOME/lib/jfxrt.jar:$KAWA_HOME/kawa.jar HelloButton1.scm

If you build Kawa from source, do:

$ ./configure --with-javafx=$JAVA_HOME --enable-kawa-frontend ...other-args...

The resulting Kawa binary sets up the path to jfxrt.jar so you just need to do:

$ kawa HelloButton1.scm

Building for Android

Google's phone/tablet operating system Android is based on a custom virtual machine on top of a Linux kernel. Even though Android isn't strictly (or legally) speaking Java, you can build Android applications using Kawa.

Below is "Hello world" written in Kawa Scheme. A slightly more interesting example is in next section.

(require 'android-defs)
(activity hello
  (on-create-view
   (android.widget.TextView (this)
    text: "Hello, Android from Kawa Scheme!")))

The following instructions have been tested on GNU/Linux, specifically Fedora 17. This link may be helpful if you're building on Windows.

Downloading and setting up the Android SDK

First download the Android SDK. Unzip in a suitable location, which we'll refer to as ANDROID_HOME.

export ANDROID_HOME=/path/to/android-sdk-linux
PATH=$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$PATH

Next you have to get the appropriate platform SDK:

$ android update sdk

You need to select an Android “platform”. Platform (API) 16 corresponds to Android 4.1.2 (Jelly Bean). Select that or whatever you prefer, and click Install. (You can install multiple platforms, but each project is built for a specific platform.)

ANDROID_PLATFORM=android-16

Building Kawa for Android

Set JAVA_HOME to where your JDK tree is. You should use JDK 6; JDK 7 does not work at time of writing.

$ export JAVA_HOME=/opt/jdk1.6

First get the Kawa source code.

If using Ant (as is recommended on Windows):

$ ant -Denable-android=true

Alternatively, you can use configure and make:

$ KAWA_DIR=path_to_Kawa_sources
$ cd $KAWA_DIR
$ ./configure --with-android=$ANDROID_HOME/platforms/$ANDROID_PLATFORM/android.jar --disable-xquery --disable-jemacs
$ make

Creating the application

Next, we need to create a project or “activity”. This tutorial assumes you want to create the project in the target directory KawaHello, with the main activity being a class named hello in a package kawa.android:

PROJECT_DIR=KawaHello
PROJECT_CLASS=hello
PROJECT_PACKAGE=kawa.android
PROJECT_PACKAGE_PATH=kawa/android

To create the project use the following command:

$ android create project --target $ANDROID_PLATFORM --name $PROJECT_DIR --activity $PROJECT_CLASS --path ./$PROJECT_DIR --package $PROJECT_PACKAGE

Replace the skeleton hello.java by the Scheme code at the top of this note, placing in a file named hello.scm:

$ cd $PROJECT_DIR
$ HELLO_APP_DIR=`pwd`
$ cd $HELLO_APP_DIR/src/$PROJECT_PACKAGE_PATH
$ rm $PROJECT_CLASS.java
$ create $PROJECT_CLASS.scm

We need to copy/link the Kawa jar file so the Android SDK can find it:

$ cd $HELLO_APP_DIR
$ ln -s $KAWA_DIR/kawa-1.14.1.jar libs/kawa.jar

Optionally, you can use kawart-1.14.1.jar, which is slightly smaller, but does not support eval, and does not get built by the Ant build:

$ ln -s $KAWA_DIR/kawart-1.14.1.jar libs/kawa.jar

Copy or link custom_rules.xml from the Kawa sources:

ln -s $KAWA_DIR/gnu/kawa/android/custom_rules.xml .

Finally to build the application just do:

$ ant debug

Running the application on the Android emulator

First you need to create an Android Virtual Device (avd). Start:

android

Then from menu Tools select Manage AVDs.... In the new window click New.... Pick a Name (we use avd16 in the following), a Target (to match $ANDROID_PLATFORM), and optionally change the other properties, before clicking Create AVD.

Now you can start up the Android emulator:

$ emulator -avd avd16 &

Wait until Android has finished booting (you will see the Android home screen), click the menu and home buttons. Now install our new application:

adb install bin/KawaHello-debug.apk

Running the application on your device

If the emulator is running, kill it:

$ kill %emulator

On your phone or other Android devude, enable USB debugging. (This is settable from the Settings application, under Applications / Development.)

Connect the phone to your computer with the USB cable. Verify that the phone is accessible to adb:

$ adb devices
List of devices attached
0A3A560F0C015024	device

If you don't see a device listed, it may be permission problem. You can figure out which device corresponds to the phone by doing:

$ ls -l /dev/bus/usb/*
/dev/bus/usb/001:
total 0
...
crw-rw-rw- 1 root wheel 189, 5 2010-10-18 16:52 006
...

The timestamp corresponds to when you connected the phone. Make the USB connection readable:

$ sudo chmod a+w /dev/bus/usb/001/006

Obviously if you spend time developing for an Androd phone you'll want to automate this process; this link or this link may be helpful.

Anyway, once adb can talk to the phone, you install in the same way as before:

adb install bin/KawaHello-debug.apk

Some debugging notes

You will find a copy of the SDK documentation in $ANDROID_HOME/docs/index.html.

If the emulator complains that your application has stopped unexpectedly, do:

$ adb logcat

This shows log messages, stack traces, output from the Log.i logging method, and other useful information. (You can alternatively start ddms (Dalvik Debug Monitor Service), click on the kawa.android line in the top-left sub-window to select it, then from the Device menu select Run logcat....).

To uninstall your application, do:

$ adb uninstall kawa.android

Other resources

(A more interesting text-to-speech example app is on Santosh Rajan's Android-Scheme blog.)

https://github.com/ecraven/SchemeAndroidOGL

Android view construction

An Android user interface is constructed from View objects. The following is an example that illustrates some features of Kawa to help write views hierarchies, The example is self-contained, and can be built and run as described in the section called “Building for Android”.

(require 'android-defs)
(activity hello
  (on-create-view
   (define counter ::integer 0)
   (define counter-view
     (TextView text: "Not clicked yet."))
   (LinearLayout orientation: LinearLayout:VERTICAL
    (TextView text: "Hello, Android from Kawa Scheme!")
    (Button
     text: "Click here!"
     on-click-listener: (lambda (e)
                          (set! counter (+ counter 1))
                          (counter-view:setText
                           (format "Clicked ~d times." counter))))
    counter-view)))

The first import form imports various useful definitions from the Kawa Android library. Using these is not required for writing a Kawa application, but makes it more convenient.

An Android application consists of one or more activities, each of which is an instance of the android.app.Activity class. You can use the activity macro to define your Activity class. The first macro argument (in this case hello) is the class name, and the others are members of the class, in the syntax of a field-or-method-decl. The sub-form on-create-view is an abbreviation for declaring an onCreate method (which is called when the Activity starts up followed by a setContentView: The body of the on-create-view is evaluated. The result should be a View expression, which is passed to setContentView.

The names LinearLayout, TextView, and Button are just aliases for standard Android View sub-classes. A few are prefined by (require 'android-defs), or you can define them yourself using define-alias.

View object allocation

To create an instance of a View class you “call” the class as if it were a function, as described in the section called “Allocating objects”. For example:

(TextView (this) text: "Hello, Android from Kawa Scheme!")

If you (require 'android-defs) that defines some special handling for View classes. You can leave out the (this) argument, which refers to the enclosing Activity:

(TextView text: "Hello, Android from Kawa Scheme!")

Event handlers

You can register event listeners on Android View objects using methods typically named setOnEVENTListener. For example setOnClickListener. When allocating an object you can leave out the set, and you can optionally use Scheme-style names: on-click-listener. The argument is an object of a special nested listener class, for example View$OnClickListener. These are single-method classes, so you can use a lambda expression and the section called “Anonymous classes” will automatically create the needed listener class.

System inquiry

home-directory

A string containing the home directory of the user.

command-line

Returns a nonempty list of immutable strings. The first element is an implementation-specific name for the running top-level program. The remaining elements are the command-line arguments, as passed to the main method (except for those flags processed by Kawa itself).

The first element will depend on how the Kawa module was invoked. Kawa uses the following rules to determine the command name:

  1. If the property kawa.command.name is set, that is used. This variable can be set on the kawa command line, for example from a script:

    kawa -Dkawa.command.name="$0" foo "$@"
    

    This variable is also set implicitly by the meta-arg option. FIXME.

  2. If we're reading a source file that starts with the Unix command-file prefix ‘#!/’ then we use the name of the source file. The assumption is that such a file is an executable script.

  3. If the Java property kawa.command.line is set, then we use that (after stripping off text that duplicates the remaining arguments). The kawa program sets this property to the command line used to invoke it (specifically the contents of the entire argv array), before invoking the java program.

  4. If the Java property sun.java.command is set, then we use that (after stripping off text that duplicates the remaining arguments), and then prepending the string "java ". The OpenJDK java program sets this property.

  5. If all else fails, the command name is "kawa".

command-line-arguments

Any command-line arguments (following flags processed by Kawa itself) are assigned to the global variable ‘command-line-arguments’, which is a vector of strings.

process-command-line-assignments

Process any initial command-line options that set variables. These have the form name=value. Any such command-line options (at the start of the command-line) are processed and removed from the command-line.

$ java kawa.repl -- abc=123 def
#|kawa:1|# (write (command-line))
("java kawa.repl --" "abc=123" "def")
#|kawa:2|# (process-command-line-assignments)
#|kawa:3|# (write (command-line))
("java kawa.repl -- abc=123" "def")
#|kawa:4|# abc
123

This function is mostly useful for Kawa applications compiled with the --main option. (It is used to set XQuery external variables.)

get-environment-variable name

Many operating systems provide each running process with an environment conisting of environment variables. (This environment is not to be confused with the Scheme environments that can be passed to eval.) Both the name and value of an environment variable are strings. The procedure get-environment-variable returns the value of the environment variable name, or #f if the environment variable is not found. (This uses the java.lang.System:getenv method.) It is an error to mutate the resulting string.

(get-environment-variable "PATH")
    ⇒ "/usr/local/bin:/usr/bin:/bin"

get-environment-variables

Returns the names and values of all the environment variables as an alist, where the car of each entry is the name of an environment variable, and the cdr is its value, both as strings. It is an error to mutate any of the strings or the alist itself.

(get-environment-variables)
  ⇒ (("USER" . "root") ("HOME" . "/"))

Processes

exit [code]

Exits the Kawa interpreter, and ends the Java session. Returns the value of code to the operating system: The code must be integer, or the special values #f (equivalent to -1), or #t (equivalent to 0). If code is not specified, zero is returned. The code is a status code; by convention a non-zero value indicates a non-standard (error) return.

Before exiting, finally-handlers (as in try-finally, or the after procedure of dynamic-wind) are executed, but only in the current thread, and only if the current thread was started normally. (Specifically if we're inside an ExitCalled block with non-zero nesting - see gnu.kawa.util.ExitCalled.) Also, JVM shutdown hooks are executed - which includes flushing buffers of output ports. (Specifically Writer objects registered with the WriterManager.)

emergency-exit [code]

Exits the Kawa interpreter, and ends the Java session. Communicates an exit value in the same manner as exit. Unlike exit, neither finally-handlers nor shutdown hooks are executed.

make-process command envp

Creates a <java.lang.Process> object, using the specified command and envp. The command is converted to an array of Java strings (that is an object that has type <java.lang.String[]>. It can be a Scheme vector or list (whose elements should be Java strings or Scheme strings); a Java array of Java strings; or a Scheme string. In the latter case, the command is converted using command-parse. The envp is process environment; it should be either a Java array of Java strings, or the special #!null value.

system command

Runs the specified command, and waits for it to finish. Returns the return code from the command. The return code is an integer, where 0 conventionally means successful completion. The command can be any of the types handled by make-process.

command-parse

The value of this variable should be a one-argument procedure. It is used to convert a command from a Scheme string to a Java array of the constituent "words". The default binding, on Unix-like systems, returns a new command to invoke "/bin/sh" "-c" concatenated with the command string; on non-Unix-systems, it is bound to tokenize-string-to-string-array.

tokenize-string-to-string-array command

Uses a java.util.StringTokenizer to parse the command string into an array of words. This splits the command using spaces to delimit words; there is no special processing for quotes or other special characters. (This is the same as what java.lang.Runtime.exec(String) does.)

Time-related functions

current-second

Returns an inexact number represent the current time on the International Atomic Time (TAI) scale. The value 0.0 represents midnight on January 1, 1070 TAI (equivalent to 10 seconds before midnight Universal Time), and the value 1.0 represents on TAI second later. Neither high acuracy nor high precision are required; in particular returning Coordinated Universal Time plus a suitable constant might be the best an implementation cat do. The Kawa implementation just multiplies by 0.001 the result of calling the method currentTimeMillis in class java.lang.System.

current-jiffy

Returns the number of jiffies as an exact integer that have elapses since an arbitrary implementation-defined epoch (instant). A jiffy is an implementation-defined fraction of a second which is defined by the return value of the jiffies-per-second procedure. The starting epoch (instant 0) is guaranteed to be constant during a run of the program, but may vary between runs. (At the time of writing, Kawa's jiffy is one nano-second.)

Rationale: Jiffies are allowed to be implementation-dependent so that current-jiffy can execute with minimal overhead. It should be very likely that a compactly represented integer will suffice as the return value. Any particular jiffy size will be inappropriate some some implementations: a microsecond is too long for a very fast machine, while a much smaller unit would force many implementations to return integers which have to allocated for most calls, rendering current-jiffy less useful for accurate timing measurements.

jiffies-per-second

Returns an exact integer representing the number of jiffies per SI second. This value is an implementation-specified constant. (At the time of writing, the value in Kawa is 1,000,000,000.)

sleep time

Suspends the current thread for the specified time. The time can be either a pure number (in secords), or a quantity whose unit is a time unit (such as 10s).

Deprecated low-level functions

These sections document older and less convenient ways to call Java methods, access Java fields, and use Java arrays.

Low-level Method invocation

The following lower-level primitives require you to specify the parameter and return types explicitly. You should probably use the functions invoke and invoke-static (see the section called “Calling Java methods from Scheme”) instead.

primitive-constructor class (argtype ...)

Returns a new anonymous procedure, which when called will create a new object of the specified class, and will then call the constructor matching the specified argument types.

primitive-virtual-method class method rtype (argtype ...)

Returns a new anonymous procedure, which when called will invoke the instance method whose name is the string method in the class whose name is class.

primitive-static-method class method rtype (argtype ...)

Returns a new anonymous procedure, which when called will invoke the static method whose name is the string method in the class whose name is class.

primitive-interface-method interface method rtype (argtype ...)

Returns a new anonymous procedure, which when called will invoke the matching method from the interface whose name is interface.

The macros return procedure values, just like lambda. If the macros are used directly as the procedure of a procedure call, then kawa can inline the correct bytecodes to call the specified methods. (Note also that neither macro checks that there really is a method that matches the specification.) Otherwise, the Java reflection facility is used.

Low-level field operations

The following macros evaluate to procedures that can be used to access or change the fields of objects or static fields. The compiler can inline each to a single bytecode instruction (not counting type conversion).

These macros are deprecated. The fields and static-field functions (see the section called “Accessing object fields”) are easier to use, more powerful, and just as efficient. However, the high-level functions currently do not provide access to non-public fields.

primitive-get-field class fname ftype

Use this to access a field named fname having type type in class class. Evaluates to a new one-argument procedure, whose argument is a reference to an object of the specified class. Calling that procedure returns the value of the specified field.

primitive-set-field class fname ftype

Use this to change a field named fname having type type in class class. Evaluates to a new two-argument procedure, whose first argument is a reference to an object of the specified class, and the second argument is the new value. Calling that procedure sets the field to the specified value. (This macro's name does not end in a ‘!’, because it does not actually set the field. Rather, it returns a function for setting the field.)

primitive-get-static class fname ftype

Like primitive-get-field, but used to access static fields. Returns a zero-argument function, which when called returns the value of the static field.

primitive-set-static class fname ftype

Like primitive-set-field, but used to modify static fields. Returns a one-argument function, which when called sets the value of the static field to the argument.

Old low-level array macros

The following macros evaluate to procedures that can be used to manipulate primitive Java array objects. The compiler can inline each to a single bytecode instruction (not counting type conversion).

primitive-array-new element-type

Evaluates to a one-argument procedure. Applying the resulting procedure to an integer count allocates a new Java array of the specified length, and whose elements have type element-type.

primitive-array-set element-type

Evaluates to a three-argument procedure. The first argument of the resulting procedure must be an array whose elements have type element-type; the second argument is an index; and the third argument is a value (coercible to element-type) which replaces the value specified by the index in the given array.

primitive-array-get element-type

Evaluates to a two-argument procedure. The first argument of the resulting procedure must be an array whose elements have type element-type; the second argument is an index. Applying the procedure returns the element at the specified index.

primitive-array-length element-type

Evaluates to a one-argument procedure. The argument of the resulting procedure must be an array whose elements have type element-type. Applying the procedure returns the length of the array. (Alternatively, you can use (field array 'length).)