Quick SILK Scheme tutorial

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.


Quick Example

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


Evaluation

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.


Numbers and string evaluate to themselves

> 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"
> 

Booleans, characters, and vectors evaluate to themselves

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.


Variables evaluate to their lexical value

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.


A function application evaluates each argument

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.


Syntax

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)