Rebol 1.0.2 User's Guide

Discovered at FTP cache: https://grumbeer.dyndns.org/ftp/www.ibiblio.org/

README.txt says Rebol 1.0.2 was released 6-October-1998

See also:


Copyright (c)1998 REBOL Technologies. All Rights Reserved.


About This Guide

This guide is a comprehensive tutorial on how to use REBOL messaging language. REBOL is a user-friendly language and you will soon be writing powerful programs.

This guide includes information about REBOL, writing and running REBOL scripts, and common applications. It then explains the concepts of REBOL values, words, series, evaluation, and context.

Finally, this guide includes useful tips on troubleshooting REBOL scripts.

This guide assumes minimal prior knowledge of computer languages. If needed, read the Quick Start Guide as an introduction to this guide. The Expert’s Guide is a dictionary of REBOL words for your reference. Or, from within REBOL, you can type help followed by the word (help print) to get a description on the display screen.

REBOL History

Carl Sassenrath, developer of the Amiga multitasking operating system and other computer tools, has had a vision for more than twenty years. He envisioned a simpler computer language that was also powerful. In addition, he wanted it to work equally well on and pass data between all major operating systems.

Carl’s vision has become a reality in REBOL. Carl, with the help of many other rebellious computer programming and support people, has produced a dynamic messaging language that is simple to learn and use, yet vastly powerful.

People and computers can now freely communicate.

About REBOL

REBOL (pronounced REB-ul) is an acronym for relative expression-based object language . REBOL is a messaging language that evaluates expressions and returns values. REBOL expressions consist of words:

show next computer-name date? today

and values (numbers, times, dates, strings, email addresses, URLs, etc.):

1234 12:45 30-Sep-1998 reb@rebol.com

grouped into blocks :

[sheriff "Bob" 34 bob@prairie.west tough-guy]

if time > 4:00 [send friends "time to REBOL"]

You can write any combination of words and values into an expression — the format is totally up to you, as long as there is a space or other delimiter separating each one.

REBOL gives you the power to define a series :

colors: ["red" "green" "blue" "yellow" "orange"]

then evaluate an expression and return a value:

>> print third colors
== blue

In REBOL, everything is a value . The type of value is called the datatype . REBOL datatypes are primitive (numbers, date, time, words, files, email) or constructed (built from primitive datatypes). Functions and objects are constructed datatypes.

A REBOL word gets its meaning from the context in which it is used. For instance, the word time in the above example might be used for different things in various parts of a REBOL script.

Each of these concepts will be developed in this guide with examples.

Writing a Script

REBOL scripts are written and exchanged as text files, allowing them to be created and modified with any text editor. The script begins with the word REBOL followed by a header block which provides information about the script. The header has many uses for documentation, archiving, revision tracking, script requirements, or other purposes. Here’s a typical header:

REBOL [
    Title: "Query a Few Friends"
    Author: "Robert Rebol"
    Date: 30-Sep-1998
]

Text which appears before the header block is ignored. Text appearing after the header is the script itself.

Running a Script

Refer to the Quick Start Guide for instructions on how to install REBOL.

To evaluate (execute) a REBOL script and see the results, you have two choices. First, you can start REBOL and type at the >> prompt:

do %filename.r

The file will be loaded and evaluated. (The % tells REBOL that the value is a file name rather than a word, function, or something else.)

Second option: you can specify a script from the shell or GUI (if your computer platform supports it):

rebol filename.r

This immediately starts REBOL running with the script.

Note: Make sure you run the script from the directory in which rebol.exe was installed.

REBOL Applications

REBOL messaging language can be used to perform a wide variety of tasks on your computer including:

  • building and managing data (lists, inventory, scheduling, addresses, contacts)

  • transferring data (email, file transfer, file backup)

  • math calculations (checkbook, business statements, trigonometry)

  • sharing data with other computer systems (REBOL files can be read by most operating systems with no conversion)

  • much more — REBOL is both a simple and a powerful messaging language.

Chapter 1: REBOL Values

About Values

Values are what languages use to represent meaning. Values are information (data) you organize, transfer, and compute. Values are a key component in all REBOL programs.

This section summarizes the values (sometimes called constants or literals) that can be directly expressed in REBOL. They include integer, decimal, money, time, date, string, binary, email, file, url, and logic values as well as none. Refer to the Expert’s Guide for complete descriptions and examples of all REBOL values.

As we communicate, we exchange meaning though a set of notations or values. We must be careful to express our thoughts in a form that the receiver understands. It makes no difference if the communication is between two people, a person and a computer, or two computers. Communication must have common values — otherwise the content is lost and the receiver gets a meaningless jumble of data.

In REBOL, most values are written in a common, natural form. Some values have alternate forms to allow for minor variations which are found in some countries. REBOL will accept any of the forms.

Value Example Values as Written in REBOL
integer 0 1234 -1234 +1234 1234567890 123’456’789
decimal 12.34 +12.34 -12.34 1. 123’456’789 12,34 -1, 0,0 1.23E4 -1.23E-4 +.1E2 1.E-2 1,23e4 -1,23e-4 1,e2 ,1e2 1,e-2 1e2 1E2
time 1:22 13:47:52 0:01.5 :5,25
date 30-Jan-1999 30-June-1957
30-1-1999 30-6-57 ;always D-M-Y
30/1/1999 30/6/57
1999-1-30
money $1234.85 $120’345 US$12.49 DKM$100’200’305,65
string "This string is only on a single line."
{ This string spans more than one line
and also allows "quoted" strings. }
binary #{3A18427F 899AEFD8}
64#{LmNvbSA8yw9CB0aGvXmgUkVCu2Uz93}
email info@rebol.com
president@oval.whitehouse.gov
file %memo.txt
%script.r
%System:Prefs/date
url http://WWW.REBOL.COM
ftp://ftp.luth.se/docs/amiga.txt
file://zen/prefs/date
mailto:info@REBOL.com
issue #707-467-8000 ;telephone number
#1234-5678-9012 12/99 ; credit card number
#0987654321-09876 ;serial number
tuple 1.2.0 (version number)
199.4.80.1 (network address)
255.255.127 (RGB color)
logic true false on (same as true) off (same as false)
none none

Using Values

Within a computer language, the types of fundamental values as summarized above are called its primitive datatypes. Programs are created by combining these datatypes in an endless variety to form more complex data.

The list summarizes the datatypes that can be directly represented in the REBOL language, but there are also several other datatypes. Functions and objects , for example, are constructed during evaluation using blocks, not through a direct expression. They are constructed datatypes . For instance, the function created by:

add2: func [num] [num + 2]

does not exist until this line of REBOL script has been evaluated. Before that, this line is only a sequence of values.

An entire REBOL script is just a set of values, which when evaluated, produce new values. And evaluation is simply a way of getting a value. This may seem trivially simple, but it is vitally important to understanding the simplicity of REBOL.

Let’s now take a closer look at how to use each of the primitive values.

Integer Values

Integer (also known as whole number) values are represented in as a sequence of numeric digits. No commas or periods are allowed. A minus sign can be used to indicate negative numbers.

0 1 1234 123456789
-1 -1234 -123456789

Note that there should be no space between the minus and the first digit.

Long number cannot be broken up by using commas, because a comma indicates a decimal point in many countries. The same is true with periods. However, REBOL does permit single quotes to separate long integers. The quotes may appear anywhere after the first number.

2'147'483'647

Decimal Values

Decimal values are represented as a sequence of numeric digits, followed by a fraction separator, followed by more digits. The separator may be either a period or a comma.

1.23 123. .321 1234.5678
1,23 123, ,321 1234,5678

Extra spaces, commas, and periods are not allowed in numbers to designate groups of thousands. Doing so could result in translation errors, evaluation errors, or misinterpretation of a value's datatype. However, REBOL does permit single quotes to separate long integers. The quotes may appear anywhere after the first number.

100'234'562.3782
100'234'562,3782

Negative decimal values are preceded by a minus sign. Note that there should be no space between the minus and the first digit.

-1.23 -123. -.123 -1234.5678
-1,23 -123, -,123 -1234,5678

Decimals can also be expressed in scientific notation format, by appending an exponent designated by the letter E or e. The exponent may be positive or negative.

1.23E10 1.2e7 -56.72E3 -.34e-12
1,23E10 1,2e7 -56,72E3 -.34e-12

Time Values

A time period (duration) or a time of day is expressed as a numeric digits separated with colons (:). Hours, minutes, and seconds can be written:

10:30 0:00 18:59 23:59:50 8:6:2

Notice that the last case does not require leading zeros for minutes and seconds. They are optional.

The minutes and seconds fields can contain values greater than 60 . When this is done, the values will be converted automatically. For instance 0:120:00 is the same as 2:00:00 .

Times may be followed by AM or PM with no space in between.

10:20PM 3:32:20AM

Sometimes, a more precise time is needed, so a fractional second can be appended. As with decimal numbers, either a period or a comma is acceptable:

2:59:29.5 0:00:08.25
2:59:29,5 0:00:08,25

This last example deserves further attention. There will often be cases when a time period is shorter than a few minutes. These can be abbreviated when the decimal point (the period or the comma) is present. For example:

1:10.25 0:20,1 :10.82 :0,5 :325.2

Note that in the last case :325.2 minutes is equivalent to 5:25.2 hours/minutes.

Date Values

Around the world, dates are written in a variety of formats. Most countries use a day-month-year order. One of the few exceptions is the USA**,** which commonly uses a month-day-year format. If a date is written numerically, such as 2/1/1999, it is ambiguous. The month could be interpreted as either February (in the USA) or January (nearly everyone else). Some countries use dashes (-), some use slashes (/), and others use periods (.) as separators. Finally, computer people often prefer dates in the year-month-day format so they can be easily sorted.

REBOL is flexible, allowing dates to be expressed in a variety of formats. The first day of March would be valid in any of the following common international formats:

1/3/1999
1-3-1999
1999-3-1
1.3.1999 ; see the following note

Note: The last format requires that the year always be expressed in full (four digits). Abbreviating the year will cause the date to be interpreted as a tuple which could cause problems in comparison and sorting operations.

To accommodate the USA, the following additional forms are permitted:

1/Mar/1999 1/March/1999
1-Mar-1999 1-March-1999

It is best to write the year in full. Otherwise, problems will occur with date comparison and sorting operations. Therefore, shortened years (e.g. 99 for 1999) can be used, but their interpretation is relative to the current year. They will span forward and backward a half century from the current year.

28-2-66 ; refers to 1966
12-Mar-20 ; refers to 2020
14-8-46 ; refers to 2046, not 1946

To represent dates in the first century (which is rarely done because the Gregorian calendar did not exist), you must use leading zeros as in 09-04-0029).

There can be no spaces within the date. For instance: 10 - 5 - 99 would be subtraction between three numbers, not evaluated as a date.

Money Values

Money values are represented by a currency designator, followed by a $, then numbers. The default currency is U.S. Dollars ($). Numbers are formed in the same format as decimals.

$1234.85
$120’345
USD$12.49
DKM$100’200’305,65

To prevent errors, be careful when performing mathematical operations between multiple currencies.

String Values

String values are written as a sequence of characters surrounded by quotes or braces. A quoted string is restricted to a single line. It may not extend past the end of a line and must not contain unprintable characters.

"This is a short string of characters."

Braced strings are used for larger sections of text that span multiple lines. All of the characters of the string, including spaces, tabs, quotes, and linefeeds (but not the braces) are part of the string.

{This is a long string of text which will not easily fit
on a single line of source. These are often used for
documentation purposes.}

Within the string, braces are counted, so a string can include other braces as long as the number of closing braces matches the number of opening braces.

Special characters and operations can be encoded into a string by flagging them with the escape character "^". This character is used rather than the backslash ("") because it avoids conflicts with file paths, which on the PC use a backslash. Special escape-sequence characters include:

escape meaning
^" inserts a "
^} inserts a }
^^ inserts a ^
^/ starts a new line
^(line) starts a new line
^- inserts a tab
^(tab) inserts a tab
^(page) new page
^C inserts control-C
^[words...] executes a REBOL block

Binary Values

Binary data sequences (binary strings) are written as a hash (#) followed by a braced string. The characters within the string are encoded in one of several formats as specified by an optional number prior to the hash. Formats include hex (16), octal (8), binary (2), and base-64 (64). Hex is the default.

Spaces, tabs, linefeeds, and comments within the string are permitted, allowing the data to be formatted in the most convenient manner.

#{3A 18 92 56}
#{3A18427F 899AEFD8}
2#{1001011011001010100101101100101101}
64#{LmNvbSA8yw9CB0aGvXmgUkVCu2Uz93}

Strings which are missing the proper number of characters are padded on the right.

Email Values

The standard form of an email address is a name, followed by an @, followed by the domain. The email address may be of any length, but must not include any of the REBOL restricted characters (brackets, quotes, braces, spaces, linefeeds, etc.). These formats are acceptable:

info@REBOL.COM
123@number-mail.org
my-name.here@an.example-domain.com

Upper and lower casing will be preserved.

Before you can send email from REBOL, you must specify the server you'll be using as well as your email address. Make sure this data is in your user.r file:

REBOL/email-port/host: "mail.yourserver.com"
REBOL/email-head/from: yourname@domain.com

The host can also be a TCP/IP network address such as:

REBOL/email-port/host: "10.20.30.40"

Sending email with REBOL is as easy as:

send dem@domain.dom {
    Hello there!
    Yes, I'm still here! Email me when you can!
    Carl
}

or

send dem@domain.dom read %letter.txt

More information on the email datatype is included in Chapter 4 - REBOL Evaluation .

File Values

REBOL files are designated with a % followed by a sequence of characters:

load %image.jpg
prog: load %examples.r
save %this-file.txt "This file is empty."
files: load %../programs/

Unusual characters in file names must be encoded with a % hex number. A file name with a space (hex 20) would look like:

%cool%20movie%20clip

Even though this is an Internet convention, it isn't very friendly, so another form is to enclose the filename in quotes.

%"cool movie clip"

REBOL converts the file name to the proper encoded format and does not include the quotes.

The standard, acceptable character for separating directories in a path is the forward slash (/) , not the backslash (). (The backslash was the "creative" contribution of Microsoft in 1982 and has confounded millions of people ever since.) However, because there are millions of people who were innocently led the wrong direction, REBOL automatically converts backslashes found in file names to forward slashes.

The natives load , save , and do will be discussed in Chapter 4 - REBOL Evaluation .

URL Values

The word URL stands for Uniform Resource Locators , an Internet standard used to access resources such as web-pages, images, files, and mail across the network. The best known URL is for web locations (http://www.rebol.com).

The first part of a URL indicates its communications protocol, called a scheme . REBOL supports the most common schemes: web pages (http: ), file transfer (ftp: ), email (mailto: ), and files (file: ). This is followed by characters that are dependent on which scheme is being used. REBOL knows about:

http://host/path/file
ftp://user:pass@host/path/file
mailto:name@domain
file://host/path/file

Some fields are optional. For instance, the host can also be followed by a port number, if it differs from the default. An ftp URL will supply a default password if one is not specified.

Characters in a URL must conform to specified standards. Restricted characters must be encoded in hex by preceding them with the escape character %.

Issue Values

An issue is a series of characters used to represent telephone numbers, model numbers, serial numbers, and other number groups. Issues start with a hash (#) and continue until the first delimiting character (such as a space) is reached.

#707-467-8000
#1234-5678-9012 12/99
#0987654321-09876

Values that are irregularly formatted (contain delimiting characters) should be written as strings rather than issues.

Tuple Values

It is common to represent version numbers, Internet addresses, and RGB color values as a sequence of three or four integers. In REBOL, these are called tuples (as in quintuple ) and are represented as a set of integers separated by periods.

1.3.0 2.1.120 1.0.2.32 ; version
199.4.80.250 255.255.255.0 ; net addresses/masks
0.80.255 200.200.60 ; RGB colors

Each integer field of a tuple may range between 0 and 255. Negative integers will produce an error.

Notice that you can specify three or four integers within a tuple. If only three are given, then the last integer is assumed to be zero. In the case where only two integers are given, you must still have at least two periods, otherwise the value will be treated as a decimal.

1.2. = 1.2.0.0
1.2.3 = 1.2.3.0

In some countries, the tuple value format is used for dates. This must be done with great care, because dates are a separate datatype.

8.6.99 is a tuple
18.6.1999 is a date

Logic Values

A datatype containing two states which represent TRUE and FALSE. They are most frequently returned from comparison functions.

The following words are predefined to hold both logic values:

true
false

on (same as true)
off (same as false)

Note that false is not equivalent to integer zero or none . The intrinsic functions of logic are:

word meaning
logic! prototypical logic
logic? return true
number? return false
make create an instance of the datatype
form format datatype into a string
mold mold datatype into a string
= equal
<> not equal
< purposeful error (see below)
<= purposeful error
> purposeful error
>= purposeful error
== equal
=? same
min purposeful error
max purposeful error
not complement
and and
or or
xor xor
random return random logic
/seed set random integer seed
~ complement

A "purposeful error" raises an error in the same manner as an unsupported datatype function. The alternate behavior of the function was considered but discarded for usability reasons (i.e. might create more user problems then help).

None

None is a datatype containing a single value which represents the state of "no value".

The word none is predefined to hold the none value. The value may also be returned from various functions, primarily those involving series (pick , find , etc.).

Note that none is not equivalent to integer zero or false . The intrinsic functions of none are:

word meaning
none! prototypical none
none? return true
number? return false
make create an instance of the datatype
form format datatype into a string
mold mold datatype into a string
= equal
<> not equal
== equal
=? same

None is also discussed in Chapter 3 - REBOL Series in the section titled Dealing with None

Chapter 2: REBOL Words

About Words

As you can see, many values can be expressed directly in REBOL. Numbers, times, dates, strings, files and several other types of values were described. These are the primary building blocks of content.

In human and computer languages we use words as symbols . By combining a small set of fundamental symbols (the letters of an alphabet), we can create an endless variety of new words, hence new symbols. Some countries use different alphabets (or even pictograms or icons); nevertheless, the concept is the same. Letters are grouped into words which symbolize our thoughts. They represent meaning.

In REBOL, too, words serve as symbols, and they are created in the same way: by combining letters (characters ). For example,

copy test! WHAT? File-Name A300 space&time

REBOL does not distinguish between upper and lower case spelling of words (the language is intended for users, not just technicians). RED , red , and Red all refer to the same word.

Words can be of any length, but they can not extend past the end of a line. They may contain alphabetic characters, numbers, and any of the characters:

? ! . ' + - * & | = _ ~

Words cannot begin with a number, nor should they contain:

, @ % ^ / \ # $ £

A space is normally used to end a word , but other types of punctuation also indicate the end of a word (the primary REBOL delimiters):

[ ] ( ) { } " : ;

Note: The current version of REBOL supports the standard 256 extended character set. International 16 bit character sets will be supported in later releases.

Refer to the Expert’s Guide for complete descriptions and examples of REBOL words.

Using Words

In REBOL, simple punctuation is used to indicate how a word is being used:

syntax meaning
word: Define a word. Give it a value.
word Evaluate a word. If it is data, get the value. If it is a function, apply it.
'word Refer to the word as a symbol. It is literally a value.
:word Get a word’s value, but don’t apply it. Useful for referring to functions without evaluating them.

The first of these defines or sets the word to associate it with a new value. In REBOL, words are defined in this fashion:

age: 72
lunch-time: 12:32
birthday: 20/3/97
town: "Ukiah"
test: %testfile.rebol

Notice that you can define a word to be any kind of value. For instance, in the example above you defined words to be integer, time, date, string, and file values. You can also define words to be more complex types of values:

people: ["Dale" "RJ" "Sam"]
code: [if age > 32 [print town]]
say: make function! [item] [print item]

These words are often called variables . However, as you will see, the concept of a word is more general than just that of variables.

To get the value of a word that was previously defined, place the colon at the front of the word. Here are the results if you typed these lines into REBOL:

>> print :age
== 72

>> if :lunch-time > 12:00 [print "hungry"]
== hungry

>> print length? :people
== 3

This is the most reliable way to get a word's value, but it is rarely done. The most common way to get the value of a word is to evaluate it (execute it or get its value). Because this is done so often, it is also referred to in REBOL as the natural value of a word. For instance, the examples above could be rewritten without the colons (:).

So what is the difference between getting a word's value and evaluating it? For simple values, there is no difference. Both produce the same result. However, for more complex datatypes such as functions, there is a big difference. One way refers to the value (function or object) and the other executes it. For instance, the word for the REBOL function print is used both ways here:

>> print "test"
== test

>> drucken: :print
>> drucken "test"
== test

The first use of print evaluates it, which causes the function to be evaluated, and the resulting value is printed. The second use of print gets its value (the function itself) and defines a new word with it. Because that word also refers to the same function, it will evaluate the same way.

The final way to use a word is as a literal . This is done when you want to refer to the word as a symbol; that is, you don't want its value nor do you want it to be evaluated. Any word can be used as a literal, regardless of whether it has a value or was never defined. Consider these:

>> print 'print
== print

>> print 'stampa
== stampa

>> prt: 'print
>> print prt
== print

This concept is similar to what human languages do when referring to a word as "itself". It is distinguished with punctuation (quotes in English).

Literals are often used when getting or evaluating a word would not produce the desired result. In the code below we search a block for a the word title . If we do not refer to the word literally, then REBOL will search for whatever value title is currently defined to be, which may not produce the desired result.

video: [
    title "Independence Day"
    length 2:25:24
    date 4/7/1996
]

>> print select video 'title
== Independence Day

If the literal quote was missing from title , then you would be using its natural value or an error would be displayed if it has no value.

Word Aliasing

Words can have alternate spellings and different character casing while referring to the same word or variable. Within REBOL this operates in two ways: identical spelling and alternate spelling.

Identical Spelling

When symbols are spelled the same way, but differ in character casing, they refer to the same word. For your convenience REBOL will keep track of the casing, even though the symbols are the same.

>> words: [TITLE title]

>> print first words
== TITLE

>> print second words
== title

>> if (first words) = (second words) [print "They're the same"]
== They're the same

Alternate Spelling

Alternate spellings can provide language localized words within REBOL. For instance, the Italian word "stampa" can be made an alias to the word "print". In REBOL, this operation makes the words equivalent, even though they are spelled differently.

The alias operation is performed at a low level so it will work for literal symbols as well as variables. Therefore, all aliases must be established prior to loading the files which use them. Aliases at the beginning of a file will not affect the remainder of the file.

The best way to initialize aliases is to create a file which is loaded from your user.r file. After that, all files will be able to use the aliased words. To create an alias, use the alias native, followed by the primary word, followed by the new name.

alias 'print "stampa"

If you have a large list of words, you might prefer to construct a block of aliases:

aliases: [
    print "intpray"
    load "oadlay"
    save "avesay"
    foreach "oreachfay"
]

foreach [word name] aliases [alias word name]

Caution: Word aliasing may cause confusion and be problematic in some cases

Chapter 3: REBOL Series

About Series

In REBOL, a group of values can form a series . Series are frequently used in computing and REBOL treats them in a consistent manner. For instance, a series could be a:

  • set of values
  • string of characters
  • directory of files
  • mailbox of messages
  • group of tasks
  • database of records (as in a checkbook)
  • sequence of images (as in a movie)
  • sequence of sounds
  • array of pixels (as in an image)
  • array of samples (as in a sound)
    and more...

The essential characteristics of a series are that it contains a set of values that are organized in a particular order .

A movie consists of a number of still images which are placed in the order that they are to be shown. This sentence contains a series of words which you read in the order that they were written.

The order implies a few simple relationships. Something comes first, something comes next. There is a beginning (head) and an end (tail). You can skip forward or backward in the series. You can count its parts (length) and you can refer to them by their positions (e.g. the fifth word in a sentence).

The following section covers taking advantage of the power of series in REBOL scripts. Refer to the Expert’s Guide for complete descriptions and examples of REBOL series.

Blocks

Both code and data can be grouped into a type of series called a block . Blocks can be directly expressed in REBOL as a set of values enclosed in square brackets:

[10:42 "write next chapter"]

Blocks can contain any number of values (up to the limit of memory) or no values at all. They can extend over multiple lines and can include any type of value, even other blocks:

[ ]

[24 37 108]

[REBOL [
    Title: "Test Script"
    Date: 30-Sep-1998
    Author: "Ema User"
]]

Note that when words are used within a block (like name, work, home), they do not need to be previously defined. Their values may be set later in the script, or in some cases, not at all (as when they are being used symbolically).

There is no difference in REBOL between blocks that hold code and blocks that hold data. Code is simply a block that is evaluated and whose words when evaluated have meaning in REBOL. Evaluation is further discussed in Chapter 4 of this guide.

if date > 31/Mar/1999 [print "project delayed"]

while [time < 10:00] [
    print time
    time: time + 0:10
]

Blocks are free form. They are not sensitive to lines, spaces or tabs. You can place lines and spacing anywhere within the block, as long as it does not divide a single value.

Note: To make programs easier to read it is helpful to indent lines that fall within a block, as shown above. Note that the closing bracket is often placed on a separate line and is not indented like the content of the block.

Decomposing

A series is created so that it can be treated as a whole. However, to be useful, you will need to access the values within it. Because the values of a series have an order, the obvious way to extract a particular value is by specifying its position. The REBOL pick function does this:

>> colors: ["red" "green" "blue" "yellow" "orange"]

>> print pick colors 3
== blue

For convenience, REBOL also provides a few shorthand functions for picking values from common positions: first , second , third , fourth , fifth , and last .

>> print third colors
== blue

>> print last colors
== orange

Traversing

In REBOL, a common way to access a series is to traverse it (to move around inside it). To do anything more than pick out values, you will need to know this.

Traversing is like crossing a creek by stepping on stones. You start by hopping to the first stone (the head), then hop from one stone to the next until you reach the end (the tail). While you are in the process of moving across the creek your current position is marked by where you are standing. The same ideas apply to traversing a series.

In the previous section, the word colors was set to a block with:

colors: ["red" "green" "blue" "yellow" "orange"]

It is crucial for you to realize that the block exists on its own, and that colors simply refers to the head of the block.

When you traverse this block, you move to other positions within it. The entire block still exists, you are just at a different "stone" (to use the creek metaphor). For instance:

>> print colors
red green blue yellow orange

You are at the head of the block. The next function will "hop" to the next position in the block and return it. You could print from the new position or define a new word for it:

>> print next colors
green blue yellow orange

>> newer: next colors
>> print newer
green blue yellow orange

What is really important here is that you are still referring to the same block, just a different position within it. If you wanted, you could use the past function to move back to the position you just passed:

>> older: past newer
>> print older
red green blue yellow orange

Traversing in this fashion is the same for all series.

To obtain a value from a different position in the block, you can use the pick function or any of its shorthand functions (first , second , third , etc.):

>> print first newer
green

If you want to move forward or backward multiple positions, you could string together multiple next or back functions:

>> print next next next colors
yellow orange

However, for general cases it is better to use the skip function:

>> example: skip colors 3
>> print example
yellow orange

You can also go backwards in a series by providing a negative skip value.

If you want to skip forward to the tail (the position just after the last value) or back to the head, the tail and head functions can be used:

>> example: head example
>> print example
red green blue yellow orange

>> print past tail colors
orange

Notice in the last example you need to move backward one (past) before printing. That's because tail always places you just past the last value.

>> tail-colors: tail colors
>> print length? tail-colors
0

As you will learn later, tail is necessary in order to insert new values in the block.

Length

The length of a series is the number of values from the current position to its tail. The length? function will determine this.

>> print length? [10 20 30 40]
4

>> skipped: skip colors 3
>> print length? skipped
2

When a block appears as a value within another block, it only counts as one value:

>> values: [new [1 2 3] old [4 5]]
>> print length? values
4

>> print length? second values
3

Position

When you are traversing a series, there will be times when you want know your position from the head of the series. The index? function will provide you with that number.

>> skipped: skip colors 3
>> print index? skipped
4

You can also check to see if you are at the head or tail positions in a series with the head? and tail? functions:

>> if head? colors [print "at head"]
at head

>> if tail? colors [print "at tail"] else [print "not tail"]

not tail

>> while [not tail? colors] [
       print [index? colors "-" first colors]
       colors: next colors
]

1 - red
2 - green
3 - blue
4 - yellow
5 - orange

Note that colors is now at the tail position in the block. To move it back to the head:

>> colors: head colors

Find and Select

With the find function you can traverse to a particular value in a series:

>> found: find colors "blue"
>> print found
blue yellow orange

>> print first found
blue

Additional attributes can be specified to allow you to control the direction (backward or forward) as well as the pattern matching (like case sensitivity and wildcards).

A block is often used to hold related values in a simple "micro database". For instance:

email-book: [
    "John" jd@great.effects.org
    "Richard" rich@photo.edu
   "Joe" walker@yoda.gov
   "Ralph" RMQ@m.falcon.edu
]

To locate a person in this database:

>> name: find email-book "Joe"
>> print [first name "is at" second name]
Joe is at walker@yoda.gov

A handy variation of find is the select function, which will return the value that follows the one that was matched:

>> print select colors "blue"
yellow

>> print select email-book "Richard"
rich@photo.edu

The select function is commonly used for finding a particular block of code to evaluate (somewhat like the switch or case statements in other languages). An example:

cases: [
    10 [print "ten"]
    20 [name: "twenty"]
    30 [quit]
]

>> do select cases 10
ten

Series Helpers

Traversing through an entire series is done often in REBOL. To help minimize the task, a few helper functions are provided.

The forall function will evaluate a block for all values from the current position to the tail of the series.

>> forall colors [print first colors]
red
green
blue
yellow
orange

Note that the first argument to forall must be a word that has been set to a series (in this case a block). As the series is traversed, the variable is updated to each position. When the function has finished, the variable is reset to its starting value.

The foreach function is similar, but is given a word and the series. For each value in the series, the word will be set to that value.

>> foreach color colors [print color]
red
green
blue
yellow
orange

The forskip function is similar to forall but skips a given number of values each time:

>> forskip colors 2 [print first colors]
red
blue
orange

Match

Without attributes, match compares to series and returns NONE if they are not the same, and the end position of the match if they are the same (which in the simple cases is usually the tail of the series). The result of a match is often passed to control functions such as if , while , or until .

str: "abcde"
if match str "abcde" [...]

If the /part function attribute is used, match will only compare the specified number of characters:

if match/part str "abc" 3 [...]

You can also specify the ending position of the match:

if match/part str "abc" find str "c" [...]

Note: Unlike the series modification functions, the position must be relative to the first series.

A match can also be made against wild card patterns. The /any attribute is used to indicate that the match can be made with any string that fits the pattern. Essentially, it allows you to use the same pattern matching features that you apply to file directories (as shown above). A '?' matches any character and a '*' matches zero or more chars.

match/any str "a*" ; any string starting with "a"

You can also use match for files, email, urls, etc.

match/any email-addr *@rebol.com
match/any file %*.r
match/any url http://www.rebol.com/*

Advanced REBOL users may need to use patterns containing the wild cards as characters. To do this, you can use /with to specify alternate wild chars with a string. The first char in the string is used as the char for single char matches (change from '?'), and the second is for any sequence (change from '*').

Creating Series

REBOL allows you to create new series with make and copy .

make creates a new series from a "prototype" and a parameter. The parameter can be an integer indicating the length (or additional length) of the series, or it can be the initial contents of the series.

str: make string! 100
file: make file! 10
file: make file! %myfile.txt

copy creates a new series by copying another. This is shorthand for make with a zero additional length parameter (make series 0). (The part option described below can also be used with this function.)

new-msg: copy message
new-msg-tail: insert (copy message) "Message: "

Modifying Series

REBOL offers a number of powerful functions to modify series. They include change, insert, remove, and clear.

change changes values within a series. If the second argument is a value, it will replace the value at the current position in the first series. If the second argument is a series, then its values will replace those of the first series. (The part , only , and dup options described below can also be used with this function.)

change block 200
change (skip block 5) 200
change tail block http://www.rebol.com
change block [200 "test"]

insert inserts values into a series. If the second argument is a value, it will be inserted into the series at the current position. If the second argument is a series, then its values will be inserted into the first series. (The part, only, and dup options described below can also be used with this function.)

insert block "start"
insert (find block 10:30) "time"
insert next block [200 "test"]

remove removes a value from a series. (The part option described below can also be used with this function.)

remove block ; removes first value from series
remove (find block fred@derf.com)

clear removes remaining values from a series.

clear skip block 20
clear find block http://www.microsoft.com
clear change name "sam" ; explained below

The change, insert, remove, and clear functions directly affect the series provided as the first argument. If you have other variables which refer to the same series, after the operation they may no longer reference the same value within the series.

The change , insert , and remove functions also return a result, which is the series as of the point just past the modification. This can be useful for making additional modifications:

>> start: "now is the time"
>> insert (remove/part find start "is" size? "is") "was"
>> print start
now was the time

; A simple replace function:
replace: make function! [series old new] [
    use [here]
    if (here: find series old) [
       insert (remove here) new
    ] else [
       print "not found"
    ]
]

In addition, options can be provided to modify functions. This is done by using function attributes (as paths, similar to those used with object references or file names):

change/part name "fred derf" 4
insert/only block [200 fred@derf.com]
change/dup space "*" 50

The valid options are:

part changes, inserts, or removes a given number of values (specified as either a count or as an ending series position).

change/part block items 5
insert/part string "new text" 3
remove/part string 5
remove/part string find string "end"
name: copy/part string 10

only treats a series value as a single value. Normally used to change or insert a block as a block, rather than as a series of values.

insert/only block ["Freddy" fred@derf.com 
change/only (skip tail block -4) ["fred" 10]

dup repeats the change or insert for the number of times specified.

insert/dup block 'none 5
change/dup (find name "fred") "*" 4
change/part/dup name string 4 3

Dealing With None

If a series function attempts to access a value that is beyond the head or tail of a series, it will return a special value, none , to indicate that there was a problem. You can use the none? function to check for such a result:

>> color: pick colors 10
>> if none? color [print "no color"] else [print color]
no color

>> color: find colors "purple"
>> if none? color [print "cannot find purple"]
cannot find purple

Another function that is often used to detect none is the none? function, which returns true if a value exists and false if the value is none .

>> color: find colors "green"
>> if none? color [print [index? color first color]]
2 green

If you do not check for none and attempt to use it with a series function, an error will occur. In the example below, next does not like the none returned by the find .

>> print next find colors "purple"
** PROGRAM ERROR: no such action (next) for none **

The functions first, second, third, fourth, fifth, and last do not return none ; they will generate an error instead. This is done for the convenience of using these functions as shorthand. If you would prefer to receive a none in such situations, use the pick function.

>> sched: [12:30 "Lunch"]
>> print third sched
** PROGRAM ERROR: value is too short **

>> print pick sched 3
NONE

Chapter 4: REBOL Evaluation

About Evaluation

As mentioned earlier in this guide, symbols are used to represent meaning. Humans can interpret the meaning of symbols and act on them. The resulting actions may not be physical. A symbol may in our minds alone invoke a particular thought or image. Nevertheless, the symbols drive the process.

You’ve learned that REBOL supports a number of values such as integers, times, dates, files, strings, blocks and others. The Values Table in Chapter 1 of this guide summarized the types of values that can be directly expressed in the REBOL language.

While these values give you a means of creating data and databases, they do not provide enough capability to do everything. There are other values within REBOL that were not shown in the table because they are not directly expressible. These values are created within REBOL and are represented as words or as series of values. For instance, earlier in this guide you were introduced to the concept of functions which perform actions and produce results. Functions like print, foreach, and first were thrown in without much explanation.

The table below summarizes these other indirectly expressed values.

Value Description
None A single value which represents the idea of no value. The word none is predefined in REBOL to hold this value.
Logic The two values which we call true and false (on and off or 1 and 0). These are often called Boolean values. The words true , false , on , off, yes , and no are defined to hold these values.
Object A series of values which exist and evaluate within a defined context.
Function A series of values which exist and evaluate within a temporary (momentary or dynamic) context.
Native An internal REBOL function that is evaluated directly by the computer. In some languages, these are called native code or primitive functions.

Some words have predefined values. Most of these words have been predefined to values that relate to some of the datatypes above. Because they cannot be directly expressed, a word must be used to represent them.

For example, the words true and false are predefined to the values for the logic states of true and false. They are symbols that stand for the values.

Words like print , repeat , and first are predefined to native function values. They perform various actions, may be given a set of arguments, and may return results. A native function's value cannot be expressed in REBOL, only the word which represents it.

Evaluating Blocks

In REBOL, there is no difference between blocks of code and blocks of data. How is it then that one gets evaluated and the other does not?

The answer is the do function. This tells REBOL to evaluate a block: traverse its values, relate them to actions, and perform those actions. The do below executes the block provided as its argument:

>> do [print 10]
10

A block is just considered to be data until it is evaluated with do or another function. You could have written:

>> todo: [print 10]
>> do todo
10

In other words, just encountering a block does not evaluate it. The do function is needed.

How are the lines of the example evaluated? There is no do for them. When REBOL reads input from the user or executes a file, a do is provided by REBOL.

The do function can also return a value after evaluating a block. For instance, you could write:

>> numbers: [1 12 123]
>> todo: [length? numbers]
>> length: do todo
>> print length
3

This can be very useful. The following example takes advantage of the value returned from the block:

align-cases: [
    left [0]
    right [page-width - length? text]
    center [(page-width - length? text) / 2]
]

>> text: "Heading"
>> page-width: 80

>> print do select align-cases 'center
36.5

There are several other useful functions which use do to evaluate blocks. They are explained below. You can also define your own functions.

Load and save are also used in evaluation. Load analyzes a value to determine the datatype, then converts the value for use by REBOL. Load is used for files, URLs, and string datatypes. For example,

load %image.jpg

Save converts and saves a value into the appropriate format for the datatype. The save function is used for files and URLs. Here’s an example:

save %this-file.txt "This file is empty."

The do, load, and save functions are useful tools as you write REBOL scripts.

Read and write are also useful functions for working with files and urls. For example,

text: read %readme.txt

will open, read the entire text file, and close it. And:

write %readme.txt "This is a readme file."

will open, write, and close the file.

Conditional Evaluation

To evaluate a block under certain conditions the if function is provided. It accepts two arguments, the first is a logic condition and the second is a block to evaluate if the condition is true.

if time > 12:00 [print "past noon"]

An if function can be followed by an optional else function which takes a single argument, the block to be evaluated if the condition were false:

if time > 12:00 [print "past noon"]
else [print [12:00 - time "hours until lunch"]]

The most common mistake in using if is forgetting to give a block as the second argument (not remembering the brackets, for instance).

The brackets are not needed if you provide a word that has been defined with a block value:

notice: [print "past noon"]
if time > 12:00 notice

That's because a block is a block, regardless of whether it is directly expressed (with brackets) or is the value of a word. It can even be the result of a function:

notices: [
    [print "past sunrise"]
    [print "past noon"]
    [print "past sunset"]
]

if time > 12:00 second notices

Here, the second function provides the block to be evaluated (or not evaluated) by if . This is possible because if is just a function that takes arguments like any other function. It is not a special purpose control symbol of the language (as it is in most languages). REBOL is classified as a functional language, because all of its executable constructs are simply functions.

Two other conditional evaluations for blocks are any and all . The any function evaluates each expression in a block to determine if any are true (according to the arguments). The all function checks to see if all the values in the block are true. If there are no values or the result is not true, a false is returned.

Repeated Evaluation

There are a variety of functions that specialize in the repeated evaluation of a block of values.

The loop function is the simplest, evaluating a block a given number of times. In this example, the prin function prints its argument without starting a new line:

>> loop 40 [prin "-"]
----------------------------------------

The repeat function extends loop by allowing you to keep track of the loop counter. The repeat must be followed by a word that will be used to hold the count value.

>> repeat count 3 [print ["count:" count]]
count: 1
count: 2
count: 3

The for function goes beyond repeat by letting you specify the starting value, the ending value, and the increment to the value. Any of the values can be positive or negative. In this example , the prin function will print its argument followed by a space but not a new line:

>> for count 0 50 10 [prin count]
0 10 20 30 40 50

You can stop the repeated evaluation of a block with the break function. This is handy if you encounter some unusual condition and do not want to continue the loop.

repeat count 10 [
    if (random count) > 5 [break]
    print "repeat"
]

For repeated evaluation involving series, the foreach, forall, and forskip functions were covered earlier in this guide.

The until function will repeat a block until the block returns the true value.

>> numbers: [1 12 1234 12345]

>> until [
       print first numbers
       numbers: next numbers
      (first numbers) > 2000
    ]
1
12
1234

And finally, the while function will repeat the evaluation of two blocks, while the first one returns true :

>> colors: ["red" "green" "blue"]

>> while [? next colors] [
       print first colors
       colors: next colors
   ]
red
green
blue

Native Functions

REBOL includes a number of predefined words called natives . A native is a function which executes directly on the system processor and requires no further interpretation by REBOL. The words print, first, loop, do are all predefined to natives.

Within a script you can create your own functions . These functions evaluate in a similar manner to REBOL natives. They will be discussed later in this guide.

All functions, whether created by you or by REBOL, can accept a number of values as arguments and return a value as a result . Of course, both of these are optional. A function does not need to accept any arguments nor return any result.

Functions can be combined to allow the result of one function to be given as the argument to another. For instance:

print first colors

The result of first would then be passed as the argument to print . When multiple functions are cascaded in this fashion, their results move from right to left. The rightmost function (first ) returns its result to the function on its left (print ).

No restrictions are placed on the datatypes of values that can be accepted as arguments or returned as results. Even values like blocks, objects, functions, and natives can be used as arguments and returned as results.

Normally a function is placed before its arguments. However, it is a common practice for most mathematic and comparison operators to be placed between their arguments. The REBOL words below are operators:

+ - * / // = <> < <= > >= and or xor

Remember when using an operator that you must include a space before and after the word to separate it from its arguments, otherwise the operator will just be treated like characters of the word.

Unlike many languages, REBOL operators and functions have no preferred ordering or precedence . However, for the convenience of users, operators will evaluate from left to right:

>> print 5 + 10 * 2
30

When a series of operations needs to be performed in a particular order you should use parentheses to indicate which is to be evaluated first. This really helps clarify the order of operations:

>> print (5 + 10) * 2
30

>> print 5 + (10 * 2)
25

Note: Ignoring REBOL’s preferred ordering can be a source of errors in your scripts.

REBOL also includes prefix operators such as add , subtract , multiply , divide , greater-than , less-than , etc. They can be used as in this example:

>> print add 10 20
30

Paths

In general, paths provide a means of refined specification . They describe a route through a hierarchy, and provide a shorthand technique for referring to sub-elements, positions, or options. They may be used with objects, functions, files, URLs, blocks, strings, and a variety of other datatypes.

Paths are expressed relative to a root value by providing a number of refinements , each separated by a slash (/). These refinements can be words (symbols), numbers, strings, files, and other datatypes. Their specific interpretation will vary depending on the datatype of the root. This is best illustrated with a few examples:

account/deposit ; evaluate an object's function

match/any ; function refinement

src/unix/main.c ; directory path to file

USA/CA/Ukiah/population ; select from multiple blocks

names/12 ; pick the 12th name from a block

Words supplied as paths are symbolic . This is necessary to allow the most intuitive form for object referencing. To use a variable, an explicit word value reference is required:

src/unix/:filename

Paths themselves can be used within paths. For instance:

dir: src/unix
data: load dir/main.c

An advanced feature of the email datatype uses email-port and email-head objects. You can make a separate object for sending email (using SMTP) like this:

REBOL/email-port: make REBOL/email-port [
    host: "mail.domain.com"
    user: "username"
    pass: "password"
]

Continuation

The catch function allows you to return to a specified point in a script using a method called continuation . A continuation is a saved point in the flow of execution that can be called and returned to at a later time. Think of it as a bookmark that saves your location and current context. Continuations are first class. They can be stored in variables, passed as arguments, and returned from functions. As such, they provide a powerful mechanism for advanced scripting in REBOL — especially for handling operations such as exceptions.

To use catch you provide a symbol and a block:

catch *symbol* *body*

The symbol is used as the name for a new function which holds the continuation point. This function becomes available within the context of the body block, where it can be called to return to the point just after the catch. Think of it as a throw function if you are familiar with that concept from other languages. It takes one argument: a value which will be returned as the result of the catch.

print catch 'throw [
    loop 100 [
         if (random 10) > 5 [throw "hit"]
    ]
   "miss"
]

The symbol throw is used here as the name of the continuation function. When it is applied, its argument is used as the return from its associated catch. In the above example, its behavior is identical to a return function.

Non-local Return

The function named by catch is local to the block passed to catch. However, there may be times when you want to return from functions called outside the block. To do so, define a word outside the context of the block to hold the continuation function.

rand-it: func [num] [
    loop num [
        if (random num) > (num / 2) [resume "hit"]
    ]
    "miss"
]

print catch 'throw [
    resume: :throw
    rand-it 100
]

Here the word resume is given the function value of throw and is used outside the block as a non-local return to the catch .

True Continuation

With the indefinite extent concept discussed later, continuations can be preserved even beyond the return point of the catch . If after the example above, you were to write the line:

resume "test"

you would return to the same point as before — just after the catch — and the "test" string would be passed to the print function. Note that the entire context of the catch is preserved. Here is another example:

times: func [num] [num * catch 'here [resume-times: :here 1]]
result: times 1
print result
if result < 100 [resume-times (result * 3)]

In this example, the catch marks the return point within the function times . When the resume-times function is applied, it passes a new value back to the multiplication. Notice that even the return point from times is preserved! The assignment to result and print result are all done again, because they follow the initial call to times .

Chapter 5: REBOL Context

About Context

Words can be used to represent values. In your script you could define the word two:

two: 2

and then later make use of it:

>> print two
2

>> print 30 + two
32

At any point during definition the set of all defined words forms the current context. In the above example, the context would include not only the word two, but the words print, +, and all other words that were previously defined by the script or by REBOL (such as its native functions). All of these words have values at this point of evaluation, and even though you are not using them all, they are part of the context because you could use them.

If the word two is used outside this context, for instance in another script or another part of the same script, it could have a different value. The context determines its meaning (its value). Because of this it is crucial for you to know the rules that establish the context of words.

In REBOL, the primary rules of context are:

  1. Every script begins with a predefined context which includes REBOL words for native functions (such as print) and system values.

  2. As your script executes, it may define new words in the context.

  3. At specific points functions, and objects may modify the context.

It is this last rule that is our next subject.

Using Context

In a REBOL script, you define various words with specific values. There will be times when you want to use a word that could already be defined elsewhere in your script. If you give that word a new value, you will destroy its previous value. For instance, in your main script the word who could represent a person, such as:

>> who: "Cindy"
>> print who
Cindy

But what if later in your script you want to use the word who again to temporarily refer to the name of a company? You may not want to lose the name of the person, but just "reuse" the word for a short time.

Whenever you begin a new block in REBOL, you can specify what words you want to temporarily redefine within that block. To do this you put the words you want to redefine in a block.

>> use [[who]
       who: "REBOL HQ"
       print who
   ]
REBOL HQ

>> print who
Cindy

The use function tells REBOL that who is to be used temporarily within the block, and you don't want to affect the value of who outside the block. It is said to be local to the block. Any number of words can be specified as local words in this manner.

You have changed the context. The word who used prior to the block now has a different meaning in the new block. This ability to change context can be quite handy, and it is applied to functions and objects as well.

Here is an example of how locals can be used in a block context. It will help illustrate a few points. In it a database of contacts is printed in a specified text format:

; A simple database:
>> persons: [
       "John Able" CEO "Digitalla" ja@dig2.com
       "Jane Baker" VP "Netescape" jane@us.gov
       "Mari Conners" COO "Micoroni" maric@mico.com
   ]

; The fields of the database as words:
>> facts: [name title company email]

; The format used to print the info:
>> text: [name "at" company "is at" email]

; The loop which prints the database:
>> use facts [forskip persons 4 [
       set facts persons
       print text
   ]]
John Able of Digitalla is at ja@dig2.com
Jane Baker of Netescape is at jane@us.gov 
Mari Conners of Micoroni is at maric@mico.com

Notice that within the forskip block the facts word is used to specify the locals. This is valid because facts is a block. Note that the same facts block is used with set which sets each of the words of the block to the values from the current position in the persons database.

Functions

The concept of a function comes from expanding on the block context. You only need two additional concepts:

  1. Functions can be evaluated without requiring do (a function's natural value is to evaluate),

  2. The initial values of local words can be supplied to the function each time it is evaluated.

For example, you might create a simple function plen to print the length of a series:

plen: make function! [series]
[print length? series]

and then use it in multiple places in your script:

>> plen "now is the time"
15

>> plen [1 2 3 4]
4

The value which follows the function is an argument. In the example, both a string argument and a block argument are used. This argument corresponds to the word series that was given in the make function! line above.

Let's take a closer look at the definition of plen. The REBOL native function make was evaluated, and it required three values:

  1. The datatype to be made, in this case a function!

  2. A block of words that will be local to the function's block and used for arguments. These appear in the order that they will appear when the function is used.

  3. The function block to be evaluated.

The new function is returned from make and is set to plen. Note that defining plen to be a function is no different than defining it to be an integer, date, or any other value. Once it has been defined, it can then be used as in the example above.

A function can also define other local words which are not used as arguments. These are placed at the head of the function's evaluation block using the use function. In the example below, name and date are local to the function, but are not arguments.

example: make function! [arg] [
    use [name date][
        ...
]]

A function can also return a value as a result of its evaluation. You can do this from any point in your function with the return native:

find-val: make function! [series value] [
    forall series [
       if (first series) = value [return series]
    ]
]

>> print find-val [1 2 3 4] 3
3 4

If you want to return from a function, but not return a value, use exit instead of return. It is similar to the break function described earlier.

When arguments are supplied to a function, they are evaluated prior to being used in the function. For instance:

>> plen next [1 2 3 4]
3

>> plen skip [1 2 3 4] 3
1

Finally, sometimes you will write a script that refers to a function before it has been defined. REBOL allows this as long as you do not try to evaluate the word before it is defined.

Func Shortcut

A function can accept any type of value as an argument. An argument can be an integer, string, date, block, or any other value, even a function or native. Here is an example in which a function is passed as an argument:

do-it: make function! [func value] [
    func value
]

Here, the word do-it will be used as a function which will accept two arguments and return a result. Because the word do-it is used as a natural value (not written as do-it or :do-it) the natural thing to do is to evaluate it.

>> print do-it :length? "test"
4

>> funcs: [length? first print]
>> print do-it second funcs [1 2 3]
1

The values of the natives passed as arguments required colons. Otherwise they would have been evaluated prior to being passed as arguments, resulting in an error.

A function can return any type of value as a result. You can make a function for creating other functions.

func: make function! [args code] [
    return make function! args code
]

The func word now refers to this function, and when evaluated, it will perform a make function! on the arguments you give it.

>> pall: func [series] [
       forall series [print first series]
   ]

>> pall [1 2 3]
1
2
3

Here, func is given two arguments: a block containing the word series and a block containing the forall. These are used by func as the args and code of the function to make.

As with everything else in REBOL, the arguments to make can be anything that evaluates to a block.

Defining Functions

New functions are created by supplying make with the prototypical function datatype (function!), a block of argument words, and the function body block. The form is:

make function! arguments body

For instance, to create a function to add two values you would write:

make function! [a b] [a + b]

The make returns the new function as a value, and it is often set to a word for later use:

sum: make function! [a b] [a + b]

This new function could then be applied to a set of arguments, as in:

>> print sum 10 2
12

Note that naming a function is completely optional. You may want to do something else with the function value, such as return it as a result from another function. As an example, take the standard REBOL helper function func which is defined as:

func: make function! [args body] [
    make function! args body
]

The first make creates a function which is used for creating functions. Its arguments are the new function's argument and body blocks, and it returns the newly created function as a result. Simple. Powerful. Useful. With it, the sum function above could be written more concisely as:

sum: func [a b] [a + b]

Returning Values

Similar to a block, a function can return its last value as a result. As shown above, this value can be any datatype, including a function value.

Should you want to return from a function at some other point within it, you can do so with return. It accepts one argument — the value to return.

sum-check: func [a b] [
    if a < 0 [return b]
    if b < 0 [return a]
    a + b
]

If you do not wish to return a result from a function, use exit.

Objects

An object is a group of words that have predefined values. An object can be referred to as a single value which can be stored in a variable, passed to functions as an argument, or returned from a function as a result.

Defining Objects

New objects are created with make, the prototypical object datatype (object!), and a block defining the words of the object. Making an object is as easy as making a block containing word definitions:

normal-text: make object! [
    font: "times"
    size: 12
    color: black
]

The new object is returned from make and can be handled as any other value. In the example above, the object is assigned to the word normal-text, but it could just as easily have been returned from a function.

The block which holds the word definition is evaluated, so it may contain complex definitions of values:

normal-text: make object! [
    font: lookup-font 'normal
    size: standard-size + 2
    color: pick color-set 4
]

For convenience, you can cascade the definition of multiple words:

default-text: make object! [size: color: font: none]

The words of an object can be defined to be any kind of value, including functions and other objects:

account: make object! [
    balance: 0
    deposit: func [amount] [balance: balance + amount]
    withdraw: func [amount] [balance: balance - amount]
]

The benefit of doing this is that the functions will evaluate within the context of the object. This allows you to encapsulate (keep together) the values and functions of the object.

Accessing Object Variables

Once an object has been created, you can access the variables within it by using paths. (In REBOL, a path is a means of refining a reference to a value.) For instance, to obtain the value for the balance in the account object above, you can write:

print account/balance

If you want to deposit or withdraw from the account, you can use its defined functions as:

account/deposit 1000
account/withdraw 20

If so desired, you can even set a word within an object:

account/balance: 2000

In addition, the set and get functions can be used to access the values of an object:

print set account 'balance 1000
do get account 'withdraw 100

These provide a standard function-based approach to access, allowing complex access expressions such as:

do get account (if thrifty ['deposit] else ['withdraw]) 100

Making Instances

Once an object has been created, you can make another instance (a duplicate) of it with the make function:

my-account: make account []

If you want to change the value of a word within the account, you can also do so:

my-account: make account [balance: 400]

Note that the initial values for a new object will be duplicated as well:

>> new-account: make my-account []
>> print new-account/balance
400

However, for more complex objects, create your own make function. This allows you to consistently initialize the object each time.

>> make-account: func [starting-balance] [
       make account [
           balance: starting-balance + (starting-balance / 20)
       ]
   ]

>> my-account: make-account 400
>> print my-account/balance
420

Extending Objects

You can extend the definition of an object to create new types of objects which are based on the original. Simply define new words within the block of the new object. These words will be added to the others that are already part of the object.

bank-account: make account [
    bank-name: "Mendocino Savings"
    owner: none
]

Now you can create a new bank account:

>> an-account: make bank-account [
       owner: "Carl"
       balance: 2000
       display: func [] [print [owner "has" balance]]
   ]

>> an-account/deposit 40
>> an-account/display
Carl has 2040

Hidden Variables

You can hide specified words from direct access by making them hidden variables when you define the object. This is can be done with either a function definition or the use native.

>> make-bolt: func [size] [
       make object! [
           set-size: func [value] [size: value]
           get-size: func [] [size]
       ]
   ]

>> bolt: make-bolt 10
>> bolt/get-size
10

This works because REBOL supports extended context. The use of hidden variables is supported by a REBOL helper function called make-object. For arguments it takes the parent object, a block of hidden variable words, and the object initialization block.

bolt: make-object object! [size] [
    set-size: func [value] [size: value]
    get-size: func [] [size]
]

Extended Context

In REBOL, variables have a quality known as indefinite extent. This allows the life of hidden variables and their context to be extended for as long as they might be needed. This is a powerful feature that makes scripting easier. For instance, in the example above, what would happen if the computation returned a block that contained the hidden variables?

result: use [total longest] [
    total: longest: 0
    foreach distance streets [
        total: total + distance
        if distance > longest [longest: distance]
    ]
    [total longest]
]

The block stored in result refers to the variables total and longest which were local to the block. In many programming languages this would be considered an error. In REBOL, the hidden variables are extended beyond the use block. Accessing their values will produce the correct results.

>> print get first result
278

These extended variables have some useful qualities. For instance, if you wanted to create two functions which share a common variable, you could write:

use [count] [
    count: 0
    count-add: func [num] [count: count + num]
    count-sub: func [num] [count: count - num]
]

>> count-add 10
>> print (count-sub 1)
9

Binding Into Context

Small databases, descriptive content-oriented blocks, and messages sent from other computers may have been created external to the context in which they must be used. As long as all of the words within these blocks are used as symbols, then there will be no problems.

However, you may want to use some of the words as variables. For instance, a user interface description might contain not only the size, color, and text of a button, but also an action block which is to be evaluated when the button is pressed. That block may contain words which only have values within the user-interface handling code. In order to operate correctly, the words must be associated with their correct values in the current context.

The bind native imports a block by binding its words to the current context regardless of where it was first defined. The new block is returned. For instance, a database might contain code that is to be evaluated in the current context:

database: bind operations-database
do select database 'button

As this chapter illustrates, REBOL is a powerful messaging language. Additional REBOL words and specifics of the REBOL language are included in the Expert’s Guide.

Chapter 6: Troubleshooting REBOL

About Troublshooting

REBOL is an easy-to-use language. However, errors can occur in REBOL scripts. This chapter offers information on identifying common errors, debugging scripts, and stopping a script as well as how to report any bugs you find so we can continually improve REBOL. There’s also information on how to contact us for technical support.

Errors

A few of the most common errors are:

  • No spaces between numbers or words (e.g. 1+2 rather than 1 + 2).

  • Not providing enough arguments to a function.

  • Forgetting to use blocks when needed (such as in if , else , or while ).

  • Referring to a word, rather than its value (when using first , second , pick , etc.)

  • Not using parentheses in expressions (e.g. if 1 + 2 = 3 [...])

  • Forgetting the "!" on proto-datatypes (e.g. function!)

  • Forgetting the preceding colon on argument words that could be functions

Debugging Scripts

For small scripts the most common approach to debugging is to print out values as your script proceeds. Using print works well for this, but in many cases you may also want to use the probe function which also returns the same value it printed

>> print [1 + 2]
3

>> do probe [1 + 2]
[1 + 2]
3

Stopping Your Script

Use quit to quit REBOL (not exit , which will be implemented for exiting a function in future releases)

Use halt to stop your script and return to the input prompt.

CTRL-C can be used to quit your program if it gets stuck in a loop. Some operating systems will not detect the key until I/O has been performed, such as a print.

Note: The use of CTRL-C to stop execution is a result of the default environment for the target platform and is not determined by REBOL.

Reporting Bugs

REBOL is very interested in the problems (bugs) you may have found. To report a bug or make a comment, send an email message (no HTML or enclosures). Please write a subject line that describes the problem. Write something like "making blocks does not work" rather than just "bug."

Email: bugs@rebol.com

We will send an automatic confirmation of each report. If you don't get the confirmation, then we may not have received your email (or your return address is wrong).

For all bug reports, be sure to give us a simple example of the problem. If it occurs in a large script, edit it down to just the bug. Please don’t send large files.

Also, be sure to include the REBOL version number as is printed in the title banner. It indicates not only the version and revision, but also the update number and the platform (OS). Version numbers have the format:

version.revision.update.platform

REBOL Versions

The REBOL version number is printed in the title banner. This number should be included in all bug reports and comments. It indicates not only the version and revision, but also the update number and the platform (OS).

REBOL 1.0.1.3

This indicates that you are running Version 1.0.1 for Windows 95/98/NT.

Version numbers have the format:

version.revision.update.platform

From a script you can obtain the version number with:

>> print REBOL/version

Contacting REBOL

You can contact REBOL Technologies in various ways:

Updates and information: http://www.rebol.com
Customer support: support@rebol.com
Postal mail: P.O. Box 1510, Ukiah, CA 95482
Telephone: (707) 467-8000
Fax: (707) 467-8005

What’s Next?

Expert’s Guide is your reference manual for REBOL. It includes a description and examples of all REBOL words.

Finally, check the REBOL web site for additional information and examples on how others are using REBOL messaging language. It’s at http://www.rebol.com.