Program structure

As previously noted, program code is represented by list structure, in this structure, each object represents some expression to be evaluated. All expressions evaluate to some value - there is no difference between statements and expressions.

Objects of most types evaluate directly to themselves. Two important exceptions are non-empty lists and symbols. Symbols represent variables and are thus evaluated to value of relevant variable.

Compound expressions

Non-empty lists represent all other useful expressions. At first, first element of such list is evaluated as expression. Resulting value can be of multiple types:

Rest of such list is used as arguments to object resulting from such evaluation.

Function calls

In case of function call, all arguments are evaluated first (in unspecified order) and then passed to relevant function. For example:

]=> (+ 1 2)
]=> (* 2 (+ 3 4))

Macro expansions

Macro is special case of function that operates on program source code during compilation. Arguments are passed without any modification into this function which can process them in any way it wants. Result of this function is then executed as program code instead of original expression.

Many control structures are actually implemented as macros that expand into simpler representations.

Special forms

Some control structures have to be implemented by special cases in interpreter and compiler. Such structures are represented by so called special forms. As is the case with macros, exact behavior is completely dependent on implementation of special form.

Defining functions and variables

Special form define allows you to define new variables. For example:

(define pi 3.141592)
(define sound "nyan!")

Also, you can use this special form to define functions:

(define (speak)
  (display "nyan!")

Functions are simplest construct that can be used to organize programs into reusable components and thus are very important. In contrast to many commonly used programming languages, functions are not only parts of program, but also usable values that can be passed around. Special form lambda evaluates to function object, which does not have any name. For example this function adds 3 to it's argument.

 (lambda (x) (+ 3 x))

dfsch supplies many built-in functions, that are parametrized by function that is passed to them as argument. For example map:

]=> (map (lambda (x) (+ 3 x)) '(1 2 3 4))
(4 5 6 7)

As functions are also values, there is no difference between variable naming function and any other variable. Previously shown function definition using define is essentially equivalent to this code:

(define speak
  (lambda ()
    (display "nyan!")

There are some important differences, but they are more relevant to implementation of dfsch than to program meaning.

As functions are values like any other, they can be also returned from other functions. This allows us to define functions, that make other functions. For example:

(define (make-adder x)
  (lambda (y) (+ x y)))

This function would allow us to rewrite previous example with map in arguably more compact or readable form (although it is disputable, in this simple case):

]=> (map (make-adder 3) '(1 2 3 4))
(4 5 6 7)

Because variables in dfsch are lexically scoped, x in defineition of make-adder reffers to it's argument, even when there are other variables named x. In a sense, variable references refer to variables whose definition is nearest.

Constructs let, let* and letrec create new variable scope. Also, these constructs allow us to define variables in this new scope. These constructs are traditionally used for temporary variables and such. Initial values of new variables are evaluated in outer scope in let case, in scope containing all preceding variables for let* and letrec evaluates initial values in newly created environment (it is called letrec, because it allows definition of local recursive functions).

(define (directory? path)
  (let ((stat (os:stat path)))
    (if (null? stat)
        (stat :isdir))))

For variables directly defined by user, two additional forms are avaiable:

(define-variable *foo* 123)
(define-constant +bar+ 456)

In both cases, variable is defined when it does not already exist (in contrast to define, which would overwriteit). define-constant also signals to compiler, that you do not intend to modify this variable. Symbols like * and + around such variable names are traditionally used to distinguish global variables and constants from other names.

Generated by docgen.scm running on dfsch 0.4.0 (bp-0.4.0-18-g88df03b) on 2012-04-16 10:00:28