Printable manual: brl.pdf (340Kb) or brl.dvi (204Kb) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Contents] | [Index] | [ ? ] |
It's fairly easy to pick up basic BRL syntax just by looking at examples. However, learning some Scheme beforehand can clarify the subtleties of examples in this manual and help you go beyond the examples on your own.
The Scheme programming language was designed with teaching in mind.
There is a very friendly tool available for learning it called
DrScheme.
Alternatively, if you're using the machine you installed BRL on, you can start Kawa Scheme from the shell or command prompt by typing this:
java kawa.repl |
Kawa Scheme will let you try Scheme/Java integration, but won't provide nearly as friendly an environment as DrScheme.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Contents] | [Index] | [ ? ] |
We'll begin, as is customary in introductions to programming languages, with the syntax for printing the words "Hello, World" on the screen:
> "Hello, World" Hello, World |
The > sign represents the prompt at which you type. It might be
#|kawa:1|#
or something else depending on your Scheme
implementation. But the result is the same. You type "Hello, World"
(including the quotes) and you get "Hello, World". This may be
surprising if you're used to other computer languages in which
"Hello World" can only be used as an argument to a command. Scheme does
not have this command/argument dichotomy. You use the term
expression for both concepts. Everything you type in a Scheme
interpreter is an expression. "Hello, World" is a what's called a
self-evaluating expression, or a constant.
Here's how you assign expressions to variables:
> (define h "Hello, World") > h Hello, World |
You may have used other languages in which variables have a special
character to mark them, e.g. $h
or @h
. In Scheme,
variable names are simple. A variable name is an expression that
evaluates to whatever you assigned to it. The define
expression
above is an example of variable assignment, more commonly called
binding in Scheme. The variable name is said to be bound to
a value.
> (string-append h "!!") Hello, World!! |
This illustrates the most common way to combine expressions. The
expression "!!"
is a constant string, much like the "Hello,
world"
you typed earlier. To the left of that is h
, the
variable we just bound to a string value. Then there's
string-append
, a function that concatenates strings.
An important thing to note here is that string-append
is just
another variable, like h
. The only difference is that h
is bound to a string value, while string-append
is bound to a
procedure value. Procedures are just another Scheme data type,
like strings or numbers.
This makes the process of evaluating (string-append h "!!")
simple and uniform. Each expression within the parentheses is
evaluated, then the value of the first expression (in this case
string-append
) is applied, i.e. invoked with the remaining
values as function arguments.
Caution: If you're accustomed to other languages in which
adding extra parentheses does not change the meaning, you'll need to
unlearn that habit with Scheme. For example h
is just the
variable h, but (h)
means to treat h
as a function with no
arguments and apply it.
Let's review how this expression is evaluated:
(string-append h "!!") |
First, each sub-expression within the parens is evaluated.
"!!"
is self-evaluating; its value is "!!"
.
h
is a variable; the value bound to it is "Hello, World"
.
string-append
is a variable; the value bound to it is a procedure.
This makes the expression equivalent to
(string-append "Hello, World" "!!") |
Next, the procedure bound to string-append
is applied to the
arguments "Hello, World"
and "!!"
, returning the value
"Hello, World!!"
.
As you can see, this is a very simple process, called procedure application. Don't be intimidated by the use of terms like procedure, arguments, variable, bound, and self-evaluating expression to describe it. I only introduce those terms here to help you read Scheme documentation later.
Not every Scheme expression works by procedure application. You might
have guessed this, as the (define h "Hello, World")
expression
could not have worked by first evaluating h and passing its value as an
argument to define. At that point, h was not defined yet and could not
evaluate to anything.
There are other examples in Scheme of expressions that are not procedure application, but in general the syntax is the same: First an open parenthesis, then what you're doing, then what you're doing it to, then a close parenthesis. Although simple and uniform, this syntax can be a little confusing at first for math:
(+ (* 2 3 4) 5 6) |
The normal math notation would be 2x3x4 + 5 + 6. You will find it less
confusing if you read +
in Scheme code as "the sum of" and
*
as "the product of."
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Contents] | [Index] | [ ? ] |
One of Scheme's great strengths is how simple it is to extend with new functions. Here's a simple example:
> (define (greet name) (string-append "Hello, " name "!!")) > (greet "Bruce") Hello, Bruce!! |
Note that there are two parentheses at the end of the definition.
One closes the string-append
expression, while the other closes
define
. Scheme code is usually written in an editor such that
when you type a close paren, the corresponding open paren is
highlighted. Also, since it is parens and not line breaks that end a
Scheme expression, definitions like the one above are often split into
multiple lines with indentation clarifying how "deep" the code is within
parentheses.
Your procedure can be used anywhere you would normally put a Scheme expression:
> (string-append "Wow! " (greet "Chris") " Long time, eh?") Wow! Hello, Chris!! Long time, eh? |
You can use your function to define other functions:
> (define (safe-greet name) (if (string? name) (greet name) (greet "NON-STRING!"))) > (safe-greet 2) |
Also demonstrated in the above example is the if
syntax. If the
first argument (string? name)
evaluates true(1), then the
second argument (greet name)
is evaluated.
Otherwise the third argument (greet "NON-STRING!")
is evaluated.
Here's a better example of if
:
> (define (price n) (string-append "just " (number->string n) (if (= 1 n) "dollar" "dollars"))) |
This demonstrates that an if
expression can go anywhere. This
may seem obvious if you're new to programming, but there are many
languages in which if
is used more restrictively. People
accustomed to such languages may be surprised to see an if
expression used as an argument to a procedure. As an exercise, rewrite
safe-greet
so that greet
appears only once in its
definition. Do this by making the argument to greet
be an
if
expression.
Note that number->string
is just a regular function name. There
is nothing special about the characters ->
in the middle of a
Scheme identifier.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Contents] | [Index] | [ ? ] |
This section will describe all but the least-used data structures in
Scheme. To start, let's look at the simplest structure: a pair.
This structure groups two items together. The items are retrieved with
the car
and cdr
procedures:
> (define c (cons 1 2)) > (car c) 1 > (cdr c) 2 |
Try doing something similar with strings instead of numbers, but do it
without looking at the above example. Memorize what cons
,
car
and cdr
do.
Did you do it? Good, we're done covering all but the least-used data structures in Scheme. But wait! We've only covered one very simple data structure. How can I say we've covered so much?
What makes pairs so powerful is that any data type can be
stored in them, including other pairs. This allows one to
string together (or, as the LISP community likes to say, cons up)
a list by putting the first element of the list in the car
and
another pair in the cdr
. This second pair has the
second list element as its car
and the rest of the list as its
cdr
, and so on. Eventually you reach the end of the list, where
the cdr
is a special marker known as the empty list.
> (define nums (list 1 2 3)) > (car nums) 1 > (cdr nums) (2 3) |
Scheme includes many tools for list processing. I will demonstrate two of them here.
> (define (square x) (* x x)) > (map square nums) (1 4 9) > (apply + (map square nums)) 14 |
As you can see, map
applies a procedure to each element of a list
and returns a list of the results. In this example, square
was
applied to 1, 2 and 3, resulting in 1, 4 and 9, respectively. The
apply
procedure was used to call the +
procedure with 1, 4
and 9 as arguments, returning 14.
The uses of map
and apply
are limitless. For example, if
you had a procedure that computed sales tax, you could use map
to
get a list of sales taxes from a list of prices, and then use
apply
to total them. But the great thing about Scheme is not the
existence of generally-useful procedures like map
and
apply
, but how easily you can create such generally-useful
procedures yourself.
For example, suppose you wanted to quiz yourself on arithmetic. Whether you're doing addition, multiplication, division or subtraction, the basic idea is the same. You take two numbers, perform an operation on them, and compare that to input. If the correct answer was input, say "Correct!", otherwise give the correct answer.
> (define (quiz op n1 n2) (display "Your answer? ") (if (equal? (op n1 n2) (read)) "Correct!" (op n1 n2))) > (quiz * 2 3) Your answer? 6 Correct! |
In other languages you would have had to write separate pieces of your
program for the various arithmetic operations. But with Scheme you've
written one general-purpose procedure that works with all of them, plus
operations that you didn't even know existed. Try it with
remainder
, lcm
and gcd
. Not bad for a five-line
program, eh?
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Contents] | [Index] | [ ? ] |
It's only fair to warn you that there are few resources out there just for learning Scheme. There are plenty of books that might seem to be presenting Scheme, but they are actually teaching Computer Science, and expecting you to learn Scheme as you go along. Your best approach if you just want to use BRL is to take one of these books and skim through anything that seems difficult to understand, focusing on the examples.
Computer Science principles will be very useful to you if you tackle a large software project, but for the most common use of BRL, i.e. web pages, it's enough just to know the basics of Scheme.
Another good way to learn Scheme is to look at the specification, R5RS. This document is really targeted at people making programs that interpret or compile Scheme, not those who simply want to program in Scheme. It is rather dense in places, and some of the examples are tricky because they demonstrate several principles at once. However, you can still find it a useful introduction if you don't let yourself get bogged down.
A third option is to simply keep reading this manual and learn from example, then go to R5RS or a textbook later.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |