This is a very quick tutorial on the Scheme side of SILK. It focuses on the syntax of Scheme for people unfamiliar with it. Using SILK in a Java application, particularly the use of (import) is described elseware.
Here's a simple Java program. It is conjectured that g() will terminate for any positive integer.
| package elf;
| public class Foo {
(define (f x) | public static int f(int x) {
(if (eq? (modulo x 2) 0) (/ x 2) | if ((x % 2) == 0) return x/2;
(+ (* x 3) 1))) | else return (x*3)+1;
| }
|
(define (g x) | public static void g(int x) {
;; Will this terminate? | // Will this terminate?
(print x) | System.out.println(x);
(if (not (eq? x 1)) | if (x != 1) g(f(x));
(g (f x)))) | }
|
(g 9) | public static void main(String[] as) {
| g(Integer.parseInt(as[0]));
| }
| }
Note that
A Scheme program consists of a sequence of expressions. Each expression is evaluated to produce a value. Expressions can also perform side effects, such as define a function. We begin by explaining the rules for evaluation of an expression. We assume you have access to a SILK listener which accepts an expression, evaluates it, an prints the result.
While Java has expressions and statements. Scheme only has expressions. Each expression produces a single value.
> 3 3 > 3.14 3.14 > "You're right!" "You're right!" > "multiple line string" "multiple line string" >
There are two types of numbers, exact, represented in SILK as Integer and inexact, represented in SILK as Double.
Unlike Java, strings can contain newline characters. The only character that can be escaped in a string is double quote:
> "\"Hello\"" "\"Hello\"" > > "\n is not a newline" "n is not a newline" >
The character "#" is used to start certain constants:
> #t ; true #t > #f ; false #f > #\c ; The char 'c' #\c > #\space ; The char ' ' #\space > #\newline ; The char '\n' #\newline > #(1 #\c 3) ; An Object[3] containing #(1 #\c 3) ; 1, 'c', and 3. >
In logical operations, any object that is not #f is considered true.
Scheme has a very liberal variable naming policy. Most special characters in Java are allowed in variable names. A variable is evaluated in its lexical environment. When typing a variable name at the SILK listener, the environment is called the global environment:
> this-environment ; The global environment
silk.GlobalEnvironment@5fc547
> + ; The primitive procedure +
{silk.Primitive +}
>
Variables like + and / have values that are their corresponding primitive procedures. We'll see how to invoke them shortly.
> frog/prince **** ERROR: Attempt to reference undefined symbol: 'frog/prince' Scheme Exception: error ERROR:Attempt to reference undefined symbol: 'frog/prince' silk.SchemeException: error at silk.SchemeUtils.error(SchemeUtils.java:158) at silk.GlobalEnvironment$2.run(GlobalEnvironment.java:74) at silk.Engine.run(Engine.java:59) at silk.Engine.eval(Engine.java:68) at silk.Scheme.readEvalWriteLoop(Scheme.java:94) at silk.Scheme.main(Scheme.java:77) >
While frog/prince is a valid variable name. It currently does not have a global value, so an error occurs. When an error occurs SILK provides what information it can, as well as a Java backtrace.
Here are some examples of applying built in functions.
> (+ 3 5) 8 > (* 3.1 4) 12.4 > (vector-ref #(1 2 3) 0) 1 > > (+ (vector-ref #(1 2 3) 0) (* 3 4)) 13 >
Each argument to the function, including the function itself is evaluated. Then the function is applied to the arguments to produce a value. Thus for example, in the case of (* 3.1 4) the following evaluations happen (in an unspecified order):
Normally in Scheme, the expression () is a syntax error because it is an application without a function. However, SILK follows the Common Lisp tradtion and lets () represent the empty list, which is the Java null.
Scheme has a few syntactic keywords that appear in function position in an expression. Such expressions have their own evaluation rules. Two examples form the quick example above include (define) and (if).
;;; Defining a global variable.
> (define x 3) ; public static int x = 3;
x
;;; Defining a function.
> (define (f x y) ; public static f(int x, int y) {
(+ (* x y) 3)) ; return x*y + 3; }
f
;;; Assignment.
> (define x 3) ; int x = 3;
x
> (set! x 4) ; x = 4;
4
;;; x must be a variable.
> (if (> x 5) (/ x 2) ; if (x > 5) return x/2;
(* x x)) ; else return x*x;
16
;;; Local variables - sequential binding
> (let* ((x 3) ; int x = 3;
(y (* 4 x))) ; int y = 4*x;
(+ (* x x) (* y y))) ; return x*x + y*y;
153
;;; Local variables - parallel binding;
> (let ((x 3) ; No Java equivalent
(y (* 4 x))) ; "(* 4 x)" Uses global value of x here.
(+ (* x x) (* y y)))
256
;;; Begin - expression grouping
; public int print(int x) {
; System.out.println(x);
; return x; }
> (begin ; {
(print x) ; print(x);
(print (* x x))) ; print(x); }
4
16
16
;;; Quote stops evaluation.
> '(+ x y) ; No Java equivalent.
(+ x y)
;;; Lambda - create a function.
> (define add3 (lambda (x) (+ x 3))) ; No Java equivalent.
add3
> (add3 7)
10
> (define (make-adder n)
(lambda (x) (+ x n)))
make-adder
> (define add3 (make-adder 3))
add3
> (add3 7)
10
;;; Local variables - recursive binding
;;; Here functions square and bump refer to each other.
> (letrec ((square (lambda (x) (* (bump x) x)))
(bump (lambda (x) (+ x 1))))
(square 3))
12
Built in data types
Scheme Java
boolean? Boolean logical functions
symbol? silk.Symbol string->symbol
char? Character
vector? Object[] vector make-vector vector-ref vector-set!
pair? silk.Pair cons car cdr set-car! set-cdr!
procedure? silk.Procedure lambda
exact? Integer + - / * modulo sin cos ...
inexact? Double + - / * modulo sin cos ...
string? char[] make-string string-ref string-set!
inputport? silk.InputPort open-input-file
outputport? java.io.PrintWriter open-output-file
Equality
(eq? x y) ; x == y
(= x y) ; Numeric equality, (= 3.0 3) -> #t
(eqv? x y) ; "same object" see definition
(equal? x y) ; x.equals(y)