NexJ Logo

Scheme language elements

Comments

Comments are preceded by a semicolon (;) and extend for the rest of the line. NexJ Studio's scheme editor provides some useful shortcuts for working with comments. These include:

  • Generate Element Comment: Alt+Shift+J
  • Toggle Comment: Ctrl+Shift+/, which works with multi-line selections


; this is a comment in scheme
Whitespace does not affect code execution.

Expressions and types

The simplest expressions are constant atomic symbols, numbers, Booleans, characters, strings, vectors, pairs, and lists.

Symbols, procedure and variable names are case-sensitive.

> "hello" ; constant string
; "hello"

> 12345.67 ; constant number
; 12345.67

> 'sym ; symbol
; sym

> '(a b c d e) ; list
; if we didn't have the ' that is, if the expression was "(a b c d e)", then 'a' would be evaluated as the function 'a' with arguments b c d and e.
; (a b c d e)

> '(a . b) ; pair
; each pair has a "head" (in this case a) and a "tail" (in this case b).
(a . b)

> #f ; boolean false - true is #t
; #f

The Boolean type represents true and false by #t and #f respectively. Any type can be used where a Boolean type is expected; any value other than #f is considered to be true, including the empty list.

Scheme uses a prefix notation. For example:

> (+ 3 4)
; 7

> (format "Mailbox {0} has {1} new messages." "shawed1" 55)
; "Mailbox shawed1 has 55 new messages."

> (string-upcase "shout")
; "SHOUT"

> (> 5 7)
; #f

Scheme's basic data structure is the  list. Lists are really lists of pairs. A pair is written as (val1 . val2). A list is a linked list of pairs, ending in null. The equivalent notation for null is (). For example,  '("a" "b" "c") is really ("a" . ("b" . ("c" . ()))) which also could be thought of as ("a" -> ("b" -> ("c" -> (emptyList)))). Lists are written as sequences of objects surrounded by parentheses. 


> '("a" "b" "c") ; this is a quoted list, the ' is equvalent to quote. For example, (quote ("a" "b" "c"))
; ("a" "b" "c")

; The quote makes Scheme treat the list as data. Without the ' character, Scheme would try to treat "a" as a function and would throw an error.


There are many functions for manipulating lists, including accessors: getters car and cdr and setters set-car! and set-cdr!. The function list-ref provides access to an arbitrary member of a list, length gives its length, and the list constructor is list. There are also procedures to reverse a list, to obtain the tail of a list, to check for list membership, and to perform key-value lookups (association lists).

> (car '(1 2 3)) ; get the head of the list (first pair)
; 1

> (cdr '(1 2 3)) ; get the tail of the list (first pair which returns the rest of the list)
; (2 3)

> (cdr '(1)) ; get the tail of the list (rest of the list in this case is null 
; ()

> (cons '(1 2) '(3 4)) ; construct a new pair
; ((1 2) 3 4)

> (list '(1 2) '(3 4)) ; construct a list
; ((1 2) (3 4))

Variables

Variables are dynamically typed and are bound to values by a define, a let expression, and a few other Scheme forms. Variables bound at the top level with a define are in global scope.

(define startTime (now))

Variables bound in a let  are in scope for the body of the let.


(let 
   ((startTime (now)) (index 0)) 
   ... 
   ; Scope of startTime and index.
   ...
)


A let* expression is used when one of the declarations in a let depends on another.

(let* 
	((lowerBound 1) (upperBound (+ lowerBound 100))) 
	... 
	; Scope of lowerBound and uppderBound
	; to start with lowerBound = 1 and upperBound = 101
	...
)

Functions

A function is a portion of code within a larger program that performs a specific task and is relatively independent of the remaining code. A function can take zero or more arguments, does some processing in its body and returns a result which is the return value of the last statement executed before exiting the body.

In Scheme, unnamed functions are created with the special keyword lambda.


(lambda (x) (* x x)) ; create a function that takes a single argument. 
; "lambda" is evaluated and returns a reference to a function that squares its input parameter

((lambda (x) (* x x)) 7) ; evaluate the head of the list, which returns a function, and apply that function to the parameter "7" -> 49

(define sq (lambda (x) (* x x))) ; create a variable "sq" and assign to it our squaring function

(sq 7) ; apply the function associated with the variable "sq" to the parameter "7" -> 49


Functions can be arguments to other functions and be returned by them. They can be assigned to variables and are created by lambda forms. For example:

Example 1
   ; Define a function called loadData with two arguments fileName and pageSize
   ;  "..." represents the function body. 
   (define loadData
     (lambda (fileName pageSize)
         ...
      )
    )
Example 2
   ; A shorter equivalent form to example 1.
   (define (loadData fileName pageSize)
      ...
    )
Example 3
   ; Shows how functions are applied
   ; The function being applied is in the first position of the list while the rest of the list contains the arguments
   (loadData "NightlyBatch.csv" 500)
Example 4
   ; The apply function takes its first argument and applies it to a list of arguments, so the previous function call can also be written
   (apply loadData (list "NightlyBatch.csv" 500))
To see a list of available functions, press Ctrl+Space after a parenthesis in a Scheme editor in NexJ Studio. Functions have documentation on their intended use.

In Scheme, functions are divided into two basic categories: procedures and intrinsics. All intrinsics are procedures, but not all procedures are intrinsics. Intrinsics are pre-defined functions in the Scheme language. These include +, -, *, /, set!, car, cdr, and other basic procedures.

> (define add3 (lambda (x) (+ x 3)))
; #<PCodeFunction:add3>

> (add3 4)
; 7

Assignment

Assignments do not create new bindings, as with let or lambda, but rather change the values of existing bindings. Assignments are performed with set!.

> (define a 1)
; 1

> (set! a 2)
; 2

> a
; 2