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