A lot of the power of REBOL comes from the fact that it is both a functional programming language and a symbolic language.

As a functional programming language REBOL uses a sequence of expressions (built from functions) that are evaluated to produce a flow of results which pass from expression to expression. REBOL has no keywords, and words evaluate depending upon context. Generally the order of evaluation is from left to right with some exceptions for special operators.

A symbolic language is one that lets you represent and manipulate symbols (words) the same as any other values of the language. The advantages of symbolic programming will become more clear as your REBOL skills improve. It is the key that unlocks the door to dialecting, allowing you create even more powerful programs with less code, as well as sending REBOL expressions around the Internet to be evaluated on other computer systems, not just your own. (Called "distributed computing".)

Whitespace characters, such as space, tab, newline, newpage act as delimiters as in English separating words from one another. They signal the end of one value and the start of another one.

As an example, this is a series of three values (integer 1, operator + and integer 2), which represent an expression:

>> 1 + 2

Notice the spaces. If we omit the spaces, we obtain:

>> 1+2

which is considered as one, syntactically illegal value.

Other characters that can take a delimiter role are: ()"[]{};.

This is important as when writing REBOL code, the language allows freedom of choice so that your program can be written entirely on one rather long line, or split and indented on multiple lines. The latter is the preferred method, and the reader is advised to follow the published guidelines on how to format source code. (REBOL/Core User's Guide - Section 5 - Style Guide)

More complex operator expressions can be written. There are two rules to keep in mind:

All operators have the same precedence.

Operator expressions are evaluated from left to right.

Example:

>> 1 + 2 * 3
== 9

Notice, that the addition on the left was performed before the multiplication. If we wanted to perform the multiplication first, we could have reordered the expression:

>> 3 * 2 + 1
== 7

Or use parentheses (always use parentheses with long formulas, you'll avoid mistakes) :

>> 1 + (2 * 3)
== 7

Let us suppose, that we wish to compare the results of two expressions: 1 + 3 and 2 + 2. We should use parentheses to achieve the desired evaluation order:

>> (1 + 3) = (2 + 2)
== true

While the first pair of parentheses is not necessary (the leftmost addition is performed first anyway), that is not true for the rightmost addition. If we omitted the second pair of parentheses, we would compare the result of the leftmost addition with 2, which is legal, but we would obtain an illegal addition trying to add 2 to FALSE:

Usually we evaluate only one expression and need one result. However, it is possible to evaluate more expressions one after another:

>> 4 + 6 7 + 8
== 15

It evaluated 4 + 6, then 7 + 8 and showed just the last one. A similar example can be written using parentheses:

>> (4 + 6) (7 + 8)
== 15

REBOL displays only the last expression, yet keeps track of all expressions.

>> do [a: 4 + 6 7 + 8]
== 15
>> a
== 10

that is the same of:

>> do [
a: 4 + 6
7 + 8
]
== 15

To get all results from two or more expressions, we can use a reduce function as follows:

>> reduce [4 + 6 7 + 8]
== [10 15]

We obtained a block containing all the collected results.

Another important case when we may need to collect some results may be the evaluation of a function, which expects some arguments. Let's take the add function as an example:

>> add 2 + 3 4 + 6
== 15

Explanation: in this case we asked the interpreter to evaluate the add function. The interpreter had to collect two arguments for the function, so it evaluated two expressions to the right and obtained two results, which could be used as arguments. It's the same of:

>> add (2 + 3) (4 + 6)
== 15

The above description is valid for functions with more or less arguments, i.e. particularly for functions taking just one argument too:

>> abs -4 + -5
== 9

Here the interpreter needed to evaluate the abs function. Therefore it evaluated the first expression to the right and obtained -9, which it used as the argument for the abs function. It's the same of:

>> abs (-4 + -5)
== 9

So use parentheses to avoid mistakes...

Because of the way how the interpreter collects the arguments for functions, it looks as if the operator expression to the right took precedence over the function evaluation.

It is easy to explain, why this is not the case when the function uses unevaluated (resp. fetched) arguments: in that case the interpreter doesn't need to evaluate an expression to obtain the value of the argument.

The console is your primary location for writing small segments of code to rapidly test out your ideas and functions. Nothing needs to be compiled and expressions are evaluated as soon as you press enter. This allows for interactive programming at the console, and aids in debugging large programs. The lack of a compilation phase allows for rapid prototyping and testing.

We saw a math example earlier, but now let's try something more complex:

>> join "Hi" "There"
== "HiThere"

The JOIN function combines two strings and returns a new string: "HiThere".

Now let's reverse all the characters in that string by inserting REVERSE in front of JOIN:

>> reverse join "Hi" "There"
== "erehTiH"

We can reverse it again:

>> reverse reverse join "Hi" "There"
== "HiThere"

Now you are observing basically how the REBOL language works:

You can manipulate returned values directly as they are output to the LEFT of a function. Values are streaming from the RIGHT to the LEFT in your program and you can manipulate them along the way.

This is a very important observation! It's possible to perform such manipulations indefinitely. This is basically how to build programs with REBOL.

As has been stated above, REBOL words can be used as symbols. In addition to that, we can have words "working" as variables, i.e. referring to other REBOL values.

Let's try that by building a multi-line book-keeping program. We want to use a word called wallet.

At first, let's see what happens when we enter that in the console:

>> wallet
** Script Error: wallet has no value
** Near: wallet

This happened, because the word wasn't assigned to a value. We need to assign a value:

>> wallet: $25

In my wallet is 25 dollars. I can return this value simply by typing:

>> wallet
== $25.00

Now I want to add 5 dollars:

>> wallet: wallet + $5
== $30.00

Being the flexible language that REBOL is, I could now write:

>> wallet: add wallet 5
== $35.00

It can get tedious to write this all the time, so we want to create a function to handle the task of adding money to our wallet. A function is simply a piece of program code that is executed, every time you type in a specific word. A simple function is created like this:

>> earn: does [wallet: add wallet $5]

It's the same code as shown above, but enclosed in square brackets called a block, fed to the DOES function, which means create a function doing this code block everytime it is evaluated. It needs to be stored, and that happens the same way as storing numbers. We assign earn to the value of the function created by function referred to by the word does.

This is one of the great strengths of REBOL, namely that storing values and functions work the same way! We are still following our right-to-left rule:

This opens up some very clever possibilities, which we'll explore in later chapters.

Our Earn function has been created. So every time you type:

>> earn
$40.00
>> earn
$45.00

It's a lot easier to type, right? But what if you want to earn a different amount each time? Like we saw earlier with REVERSE and JOIN, those functions take one or more arguments. Therefore we'll need to use FUNC instead of DOES.

>> earn: func [amount] [wallet: add wallet amount]

Note that another block is used before our program code. This is where we store function arguments, also known as an argument list. The number 5 has been replaced in the code by amount, which is a variable from the argument list. You can take as many arguments as you want from the argument list and use them as many times as you want in your function code.

We can equally create a Spend function:

>> spend: func [amount] [wallet: subtract wallet amount]

Now you can use them just like any other function:

>> earn $10
== $45.00
>> spend $25
== $20.00

Did you notice that we actually used a real $ sign in the return value for EARN and SPEND? That's called a datatype, which basically means that REBOL in fact recognizes the number as an actual money amount! This is one of the many strengths of REBOL, which we'll learn more about later.

You might now be asking, why does REBOL use a different syntax for variable assignment? After all, most languages use an equal sign like this:

number = 10

Why does REBOL write it this way:

number: 10

You are asking a wonderful question! It turns out REBOL does not do it just to be different, it is an important part of the language.

As we hinted early, under the hood, REBOL is an advanced language. It's a step beyond most other languages because REBOL integrates the concepts of code, data, and metadata (data that describes other data) into a single language. (We will talk about that later when we cover "dialecting".)

But for now, think of it this way. When you write:

number = 10

You are stating:

variable assignment-operator value

But, when you write:

number: 10

You are stating:

variable-defined-as value

This fact allows REBOL's variable "definitions" to stand out as special datatypes of the language. The definition is unique and is not dependent on the meaning of an operator (the = sign). This is a powerful concept that is useful when you are dealing with advanced code-as-data and metadata.

If you still have trouble relating to this notation. Think of it this way: what is more common in written human languages? In fact, if you look at an email header or an http header, how are value fields expressed? The REBOL way. Why is that?

The console allows you to work with multiple lines of code for a single expression. If you begin a block with [ and press enter, the console will not cease to accept input, until you give an equivalent ].

To let you know the console is now accepting multi-line input, the prompt changes from >> to the delimiter you are currently using, e.g. [, and it will stay that way, until the ] comes.

>> todays-earnings: [
[ $25.00
[ $30.00
[ $14.00
[ $10.00
[ ]

Multi-line strings are also possible, but note that multiline strings use the { } instead of " ".

This happens because the salute variable is initialised to the literal string "Dear ", which is kept in the body of the function, and becomes a subject to change. The append function alters that string by adding the actual argument string to it.

You can verify this by examining the dear function before it is first evaluated:

To ensure that the salute variable is correctly initialised each time, we need to keep the string "Dear " in the function body unaltered. To protect the string in the function body we can assign just a copy of it to the salute variable as follows:

Here the infix operator < takes precedence over the length? function. The result which is a boolean value is then passed to the function length? which is in fact expecting a series argument instead of a boolean.

To circumvent this, the expression can be rewritten as

if (length? series) < 10 [print "less than 10"]

or as

if 10 > length? series [print "less than 10"]

In the latter version, the infix operator takes precedence, and evaluates 10 and then length?. Since length? requires one argument, it consumes series and returns the value to > to give the correct evaluation.

>> help append
USAGE:
APPEND series value /only
DESCRIPTION:
Appends a value to the tail of a series and returns the series head.
APPEND is a function value.
ARGUMENTS:
series -- (Type: series port)
value -- (Type: any)
REFINEMENTS:
/only -- Appends a block value as a block

Here you can see that "value" is the second argument. That's what is missing.

Another common argument mistake is to provide too many arguments. This type of mistake is more subtle, because you won't get an error message.

Here is an example. Let's suppose you have the expression:

if num > 10 [print "greater"]

But, then you decide you want to print "not greater" for the else case. You might be tempted to write:

if num > 10 [print "greater"] [print "not greater"]

This is an error, but when you try it, you won't get an error message. The second block will be ignored. That's because the if function only takes one block. If you want both blocks, you should use the either function as shown here:

either num > 10 [print "greater"] [print "not greater"]

Keep an eye out for these kinds of errors in your code.

Note: There is a very good reason why REBOL allows these "extra" values without producing an error. It is actually one of the special features of REBOL. Consider the reduce function that was mentioned above. Allowing multiple expressions to follow each other allows you to create special data results that can be very useful. This is an advanced topic, but take a look at the example below to get an idea of its importance in REBOL:

Even in the console, you can produce quite long sequences of functions and variables. If you put it all in one line, it can be hard to read, especially since REBOL makes it unnecessary to using parantheses for evaluation:

print multiply add 4 add 6 5 divide 1 square-root 3

The trained eye may know where to start, but such a mixture of using functions inside arguments can quickly become hard to read.

One method is to split the code into multiple lines. With REBOL you are completely free to do that: