Header Bar

Starting To Learn Computer Programming with Rebol

Fundamental General Coding Structures, Concepts, and Apps, Demonstrated Using R2
By: Nick Antonaccio

Contents:

1. Installing and Using Rebol
1.1 Using the Rebol Console
1.2 Using Rebol's Built In Code Editor
2. Programming Basics
2.1 Functions
2.2 Running Complete Programs in Files and in the Console
2.3 Data Persistence
2.4 Variables
2.5 Conditional Evaluations
2.6 Concatenation
2.7 Lists
2.8 Loops
2.9 User Interfaces
2.10 User Created Functions
2.11 Objects
3. Rebol Dialects and Other Features
4. Some Practical GUI Dialect, File, and List Examples in Rebol
4.1 Building a Basic CRUD (create, read, update, delete) Contacts App
5. A Few More Complete App Examples
5.1 Tip Calculator
5.2 Days Between 2 Dates
5.3 Image Effects
5.4 Send Emails
5.5 Tile Game
5.6 Generic Calculator
5.7 Coin Flip
5.8 Additional Example Apps to Study
6. Graphics
6.1 Rebol "Draw" Basics
6.2 A Few Short Paint Apps
7. More About Series - Adding More Features to the Contacts App
7.1 Built in Help
8. A Few More Short Apps Using Series and Loop Constructs
8.1 Image Slideshow
8.2 Bar Chart
8.3 Paypal Report
8.4 Tric Trac Game
8.5 Shopping List and To-Do List
8.6 Calendar
8.7 Pig Latin Generator
8.8 Marching Alien Logo Army
8.9 Catch Game
8.10 Guitar Chord and Scale Diagrammer
8.11 Additional Series Apps to Study
9. Creating Web Applications using REBOL CGI
9.1 An HTML Crash Course
9.2 A Standard CGI Template to Memorize
10. Example CGI Applications
10.1 Generic CGI App, With HTML Form
10.2 CGI Photo Album
10.3 CGI Text Chat
10.4 A Generic Drop Down List Application
10.5 Summary
11. Learning More

1. Installing and Using Rebol

To write, run, test, and deploy code using any programming language, you need to set up a 'development environment' for that language. Most development environments typically include an "SDK", IDE or code editor, compiler or interpreter, documentation and/or help files, etc. Rebol's development environment takes less than a minute to set up:

Download and install REBOL from http://www.rebol.com/download-view.html.

(Be sure to download REBOL/View - not REBOL/Core)

You can use any text editor such a Windows Notepad, or a programming editor such as Notepad++, Textmate, Emacs, etc., to write code (Rebol also has a built-in editor that can be used to edit code).

All Rebol programs must begin with the header "REBOL []". Try pasting the following code into a blank text document, and save it to a file name ending with ".r":

REBOL []
alert "Hello World"

If you have Rebol installed, any code file with the .r extension will run just like an .exe file (executable program). For example, in Windows, you could save the program above to your desktop as "myapp.r" and click the myapp.r icon with your mouse, and it will run.

1.1 Using the Rebol Console

For simple little programs, you can paste code directly into the Rebol interpreter console:

  1. Click the Rebol program icon (found by default on your desktop, or in Start -> Programs -> Rebol)
  2. Click the "Console" button in the Rebol program.
  3. Type or paste your code (text) at the flashing command prompt.

When you paste code into the console, there's no need to include the REBOL [] header. A one-liner like the example above is simple to paste into the console:

alert "Hello World"

TRY THIS: Click the Rebol program icon. On the main Rebol "Viewtop" screen, click "User" -> uncheck "Open Desktop on Startup" -> Click "Save", and Rebol will automatically open to the console every time it starts.

1.2 Using Rebol's Built In Code Editor

Type the following code into the Rebol console, and a built-in mini text editor program will pop up:

editor none

You can use this program to edit code (or any other text files). Press the [F5] key on your keyboard, and the code file you're editing will be saved and executed by the Rebol interpreter. Since Rebol runs on so many operating systems, the built in editor is nice to have. With it, you don't need to install any additional programs, or know anything more about how to operate a foreign computer system. The tiny 1/2 megabyte Rebol interpreter is all you need to write and run Rebol apps.

2. Programming Basics

Computers help us manipulate DATA: useful collections of numbers, text items, and binary information such as graphics and photos, sounds, videos, hardware control codes, etc. So, learning to write code is all about learning to manipulate DATA.

Most programming languages have a few basic language structures and fundamental concepts in common, which allow you to manage and process data. For example, almost all software development tools make use of variables, functions, conditional evaluations, lists, loops, objects, user interface components, and some common processes such as concatenation, some form of persistent storage, etc. If you're just starting out programming, it's easy to get lost and stuck studying those basics, or even the basics of typing in code and delivering a working program. Rebol helps to simplify the basics, and provides a number of features which make it even more versatile and powerful than other popular programming tools. Below are some quick explanations about the fundamental concepts found in almost all programming languages.

2.1 Functions

Functions are words that perform actions upon data "parameters" (or "arguments"). There are a variety of functions built into Rebol. The 'print function prints some text to the command line console. In the following code, 'print is the function, "Hello World" is the parameter. Try pasting this example into the Rebol interpreter console:

print "Hello World"

The 'alert function alerts the user with some text (a pop-up message):

alert "Hello World"

The 'request-text function gets some text from the user:

request-text

The 'editor function starts the little text editor app. To start the editor without a specified file, use "none" as the parameter:

editor none

In Rebol, some functions have optional "refinements", which allow the function to accept a variety of potentially useful parameters. Here are some refinements of the 'request-text function:

request-text/title "Name:"
request-text/title/default "Password:" "Secret"

2.1.1 Return Values

The 'now function returns the current date and time:

now

You can take the output (the "return" value) of the 'now function, or any other function, and 'print it to the console:

print now

Similarly, you could take the return value of the 'request-text function and 'alert it right back to the user:

alert request-text

Note that before the 'alert function above can complete its operation, a value first needs to be returned from the 'request-text function. Because of that requirement, when this code runs, the alert function is held up temporarily, and the first thing the user sees is the 'request-text popup.

2.2 Running Complete Programs in Files and in the Console

To create a program (app, script, application, etc.), you'll put a number of functions and data values together into a text file and save it (this is the "code" of a program). Rebol code always starts with the "REBOL []" header on the first line. Save your program code to any file name with the ".r" extension, and click the file icon to run it:

REBOL []
print "Hello World!"
print "The current date and time is:"
print now
request-text/title "Enter some text to let me know what you think:"
alert "Thank you for sharing your thoughts!"

When you run simple code examples in the Rebol interpreter console, you can see all function return values printed out. This is a great way to test parts of a program, to check function syntax, to experiment with example bits of code and data, etc.:

now

To run the code above as a program saved in a code file, you must include the REBOL [] header. And in this case, you should print the return value, and end the program with the 'halt function. Otherwise the program will print quickly, and the interpreter will finish and close so fast that you use will likely only see an instant flash on the screen. The 'halt function stops the interpreter from closing, so that the user can see the output results. Try saving and running the following script with and without the ending 'halt, to see the difference:

REBOL []
print now
halt

2.3 Data Persistence

Most programs need to store data, so that information can be saved and loaded later. "Database" systems are a common vehicle for persistent data storage (data that doesn't get erased when a program ends), and Rebol supports a number of database models: "DBMS"s such as MySQL, SQLite, MSSQL, etc. Learning to use database sytems requires learning the "SQL" language, but Rebol has a simpler native ability to store, retrieve, and manipulate persistent data. To save any text data to a hard drive, thumb drive, web server, etc., use the 'write function. File names are preceded by the percent symbol (%):

write %mydata "This is some text that I want to save for later."

To retrieve the text later, use the 'read function. You can use the 'print function to print formatted data read from a file, or the 'probe function to view the raw data without any formatting:

print read %mydata

probe read %mydata

You can read and write to servers on the Internet the exact same way you read and write to local drives on your computer:

write ftp://user:pass@site.com/www/mydata.txt "text saved to a server"

read http://site.com/mydata.txt

You can read and write binary file data like images, sounds, videos, etc., using the /binary refinement:

read/binary http://rebol.com/bay.jpg

For more complex data formatting, use the 'save function:

save %mycontacts ["John" "Bill" "Jane" "Ron" "Sue"]

save/png %myimage.png logo.gif

To load saved data, use the "load" function:

probe load %mycontacts

probe load %myimage

probe load http://rebol.com/bay.jpg

You'll see that Rebol provides all the required abilities to search, sort, compare, combine, add, remove, change, load, save, and otherwise manipulate persistent data. It provides the same end results as database systems, with a shorter and more consistent learning curve, and a tiny, 100% portable tool set.

2.4 Variables

Variables assign a label (a word or some series of characters) to data used in a program. In Rebol, variable labels are created with the colon symbol. Try pasting these examples into the Rebol console:

REBOL []
name: "John"
alert name

Variables can be changed to hold different data. The code above alerts "John". The code below alerts "Bill":

REBOL []
name: "Bill"
alert name

You can assign a variable (word label) to text returned by requestor functions. This code alerts whatever name the user types in:

REBOL []
name: request-text/title "Name:"
alert name

You can assign a label to data read from a file:

REBOL []
data: read %mydata
alert data

2.5 Conditional Evaluations

Most programs make use of "if/then" evaluations: if (this) is true [do this]. Rebol has some typical constructs:

REBOL []
if now/time = 8:00am [alert "Time to wake up!"]
if account < $0 [alert "Your account is overdrawn."]

"Either" is like if/else evaluations in other languages: if (this) is true [do this] [otherwise do this]. Notice that each separate action block is indented, so that you can clearly see two 2 differentiated actions:

REBOL []
pass: request-text/title "Enter Password:"
either pass = "secret" [
    alert "Welcome back."
] [
    alert "Incorrect password."
]

"Case" can be used to choose between a variety of actions, depending on the situation:

REBOL []
name: "john"
case [
    find name "a" [alert {Your name contains the letter "a"}]
    find name "e" [alert {Your name contains the letter "e"}]
    find name "i" [alert {Your name contains the letter "i"}]
    find name "o" [alert {Your name contains the letter "o"}]
    find name "u" [alert {Your name contains the letter "u"}]
    true [alert {Your name doesn't contain any vowels!}]
]

2.6 Concatenation

Concatenation is the process of joining together multiple pieces of data. Rebol uses the 'rejoin function to concatenate:

REBOL []
alert rejoin ["Current Date and Time: " now]

You'll often join data represented by variable labels (often gotten from user input), values based on conditional operations, etc. Notice that rejoined items can be placed on multiple lines. When this happens, the items are indented for readability:

REBOL []
name: request-text
birthday: request-date
clr: request-color
fl: request-file
alert rejoin [
    uppercase name ", your birthday, color and file choices were: "
    birthday ", " clr ", " fl ", AFTERNOON: "
    either now/time > 12:00pm ["yep"]["nope"]
]

2.7 Lists

Most apps deal with some sort(s) of data list(s): lists of coordinates in a graphics program, lists of items (and their price, size, shipping date, etc.) in an inventory program, lists of names (and their account numbers, passwords, etc.) in a financial program, lists of files in a utility program, emails in an email program, etc. Rebol's main list structure is called the "series", and there are a number of functions built into Rebol which allow you to add, remove, change, search, sort, compare, combine, load, save, and otherwise manipulate series data. Rebol makes use of the exact same series functions when dealing with lists of nearly any type of data. In Rebol, manipulating characters within strings of text (lists of characters), graphics on screen in a game (list of images and coordinates in a draw block), data coming into and out of a network port, files on a hard drive, emails in an inbox, etc. all require knowledge of only one simple set of functions, and one unified way of thinking. The approach to dealing with lists of data is much more consistent, and uses much simpler syntax than you'll find in other languages (arrays, linked lists, and often, a whole variety of incongruent constructs to deal with different types of data).

The basic code structure used to store series of data items in Rebol is called the "block". Just put square brackets around a list of items, and assign it a variable label:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
codes: [2804 9439 2386 9823 4217]
dates: [21-dec-2013 1-oct-2012 1-jan-2014]
files: [%employees %vendors %contractors %events]

You can pick, find, and sort items from the list using simple functions and constructs:

print pick names 3
print names/3
print find names "Dave"
print sort names
print sort codes
print sort dates

Notice in the sort examples above that each type of data is automatically sorted appropriately (names alphabetically, numbers ordinally, dates chronologically).

Save and load blocks of data using the 'save and 'load functions:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
save %mynames names
loaded-names: load %mynames
probe loaded-names

You'll learn much more about working with series as you learn Rebol. These functions will become your friends: pick, find, at, index?, length?, append, remove, insert, extract, copy, replace, select, sort, reverse, head, next, back, last, tail, skip, change, poke, clear, join, intersect, difference, exclude, union, unique, empty?, write, read, save, load.

Mastering series functions and manipulation concepts is one of the core skills required to become proficient with Rebol.

2.8 Loops

Loops are used to do something to or with each item in a list. 'Foreach is the most commonly used type of loop in Rebol:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
foreach name names [alert name]

You can set variable labels in loops to any arbitrary word or series of characters. For example, just like the block of names above does not need to be called 'names, the variable used to represent each item does not need to be called 'name. This example does the exact same thing as the code above:

REBOL []
n: ["John" "Dave" "Jane" "Bob" "Sue"]
foreach i n [alert i]

Very often, you'll check each item in a list for a condition, and do something appropriate based on the evaluation of each item. Notice that the block of loop actions is indented in each of these examples:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
foreach name names [
    if find name "j" [alert name]
]

REBOL []
dates: [21-dec-2013 1-oct-2012 1-jan-2014]
foreach date dates [
    if date > 31-dec-2013 [alert form date]
]

You can structure data in a list as rows and columns:

REBOL []
mycontacts: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]
foreach [name address phone] mycontacts [
    print name
]

You can also use 'repeat and 'for loops to count through the length of items in a list. This example sets a counter variable 'i to count from 1 to the length of the names block, incrementing the variable by 1 each time through the loop:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
repeat i length? names [
    alert rejoin ["Name #" i ": " pick names i]
]

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
for i 1 length? names 1 [
    alert rejoin [
        "Current Name #" i ": " pick names i
        " Next Name: " pick names (i + 1)
    ]
]

A 'forever loop just repeats forever, until a 'break function is encountered:

REBOL []
count: 99
forever [
    print rejoin [count " bottles of beer on the wall"]
    count: count - 1
    if count = 0 [break]
]

2.9 User Interfaces

2.9.1 Faceless Apps

Certain types of utility scripts may not require any interaction with a user. For example, apps that run in the background to perform scheduled file maintenance, to upload routine data backups, to check and update the current system time, etc., don't even necessarily need to show you that they're running.

2.9.2 Console Apps

Some types of simple scripts may just provide quick "console" data printouts, or may require a brief answer to a question asked at the console text prompt. In Rebol, you can use the 'ask and 'print functions to do simple console i/o:

REBOL []
user: ask "Username (iwu83):  "
pass: ask "Password (zqo72):  "
either (user = "iwu83") and (pass = "zqo72") [
    print rejoin [newpage "Welcome back"]
] [
    print rejoin [newpage "Incorrect Password"]
    halt
]
print "You're in..."
halt

2.9.3 GUI Apps

For most "apps", a GUI (graphic user interface) screen is used to input and output data and to control program flow. Buttons, text fields, drop-down selectors, and other "widgets" allow the user to operate the program. Rebol provides a simple built-in code dialect for creating interactive GUIs. The words "view layout" create a window layout. A block holds widget descriptions:

REBOL []
view layout [
    btn
    field
    text "REBOL is really pretty easy to program."
    text-list
    check
]

You can adjust the visual characteristics of any widget in a screen layout by following each widget with appropriate modifiers:

view layout [
    btn red "Click Me"
    field 400 "Enter some text here"
    text font-size 16 "REBOL is really pretty easy to program." purple
    text-list 400x300 "line 1" "line 2" "another line"
    check yellow
]

You can have widgets perform functions or any code when clicked, or otherwise activated. Just put the functions inside another set of brackets after the widget. This is how you get your GUIs to 'do something':

view layout [
    btn "click me" [
        alert "You clicked the button."
    ]
]

The word "value" refers to data contained in a currently activated widget:

view layout [
    text "Some action examples.  Try using each widget:"
    button red "Click Me" [
        alert "You clicked the red button."
    ]
    field 400 "Type some text here, then press [Enter] on your keyboard" [
        alert value
    ]
    text-list 400x300 "Select this line" "Then this line" "Now this one" [
        alert value
    ]
    check yellow [alert "You clicked the yellow check box."]
    btn "Quit" [quit]    
]

You'll need to learn a lot more about creating GUI screens if you want to create user apps with Rebol, but you can get a lot done with just the basics demonstrated above.

2.9.4 CGI Web Apps

Rebol also provides "CGI" capabilities, so that scripts can run on a web server, accept input from HTML web forms, and print HTML responses to appear in the user's web browser.

2.10 User Created Functions

Most languages allow programmers to add features using custom defined functions. You can create your own functions in Rebol using the "func" code structure. Your own functions can contain any series of existing functions and calculations to perform work on data, just like a mini program. Give new functions a word label using the colon symbol. Give variable labels to the data parameters which the function will process. Here's a function called 'triple which multiplies any given number parameter (called 'x here) by 3:

REBOL []
triple: func [x] [
    print 3 * x
]

Now you can apply the function to any parameter values, as if that function was a native function word built into the Rebol language:

triple 4
triple 5
triple 6
alert "I just tripled the numbers 4 (12), 5 (15), and 6 (18)"

Think of the 'x argument (parameter) in the function definition above just like some program's command line option - it's just some variable data that you send to the program to do some work with. In the 'triple function, the argument is whatever number is going to be multiplied by 3, and 'x is the variable placeholder that's used to represent that parameter value.

2.10.1 Return Values

The last value in a function definition is its return value. The 'check function below takes a list as its parameter (labeled 'lst here), and checks to see if it contains any bad words (here, words with the characters "--"). It starts out by setting the variable 'answer to "safe", then uses a 'foreach loop and an 'if condition to see if "--" is found in any of the items. If at any point the bad characters are found, the answer variable is set to "unsafe". At the end of the function the 'answer variable is returned. The function is then run on both the names1 and names2 lists, and the user is alerted with the returned results:

REBOL []
check: func [lst] [
    answer: "safe"
    foreach l lst [
        if find l "--" [answer: "unsafe"]
    ]
    answer
]
names1: ["Joe" "Dan" "Sh--" "Bill"]
names2: ["Paul" "Tom" "Mike" "John"]
alert rejoin ["The list " names1 " is " check names1]
alert rejoin ["The list " names2 " is " check names2]

2.10.2 Libraries

You can save collections of useful functions ("libraries") to a file, and import them by "do"ing the file. This saves you from having to retype or paste the code of commonly used function definitions into each new program. If you were to save the function code above in a file named "myfunctions.r", the program above would look like this:

REBOL []
do %myfunctions.r
names1: ["Joe" "Dan" "Sh--" "Bill"]
names2: ["Paul" "Tom" "Mike" "John"]
alert rejoin ["The list " names1 " is " check names1]
alert rejoin ["The list " names2 " is " check names2]

Imported files can be found on the local file system (i.e., on your hard drive, a thumb drive, etc.), or at an Internet URL. For example, if you save the "myfunctions.r" file on your web site server, you could import it like this:

do http://site.com/myfunctions.r

You can actually include any code you want in imported files (not just function definitions). You could put the entire program above into a file on your web site and run it by "do"ing the URL of the file. Importing a file with 'do is exactly the same as copying and pasting the contents of the file into your code, and then executing it.

2.11 Objects

Objects are code structures which contain collections of data (variables) and actions (functions). The functionality needed to perform common types of software development work is often packaged inside object structures in "class libraries" (collections of pre-written code components). For example, Rebol's GUI widget definitions can be found in the built-in 'svv object library. You can inspect the 'svv object with a question mark:

? svv

Rebol uses forward-slash "path" notation to refer to functions and variables in any object (most other languages refer to functions as "methods", and use period characters to refer to items in an object). The following code refers to the 'button object inside the 'vid-styles portion of the 'svv library:

? svv/vid-styles/button

You can use the word "do" to import libraries of object code found in local files, or at URLs:

do %rebgui.r
do http://re-bol.com/rebgui.r
? ctx-rebgui

If you try running the last line above before importing the rebgui library, you'll see that the 'ctx-rebgui object doesn't otherwise exist. (The rebgui library contains an entire GUI language with some additional features not found in Rebol's built-in 'svv library).

2.11.1 "Encapsulating" Code in Objects

The code below defines 2 new objects, labeled 'money and 'place, The exact same labels "bank" and "var" in these objects refer to totally different pieces of information and actions:

REBOL []
money: make object! [
    var: 1234.56
    bank: func [] [
        print ""
        print rejoin ["Your bank account balance is:  $" var]
        print ""
    ]
]
place: make object! [
    var: "Wabash"
    bank: func [] [
        print ""
        print rejoin [
            "Your favorite place is on the bank of the:  " var]
        print ""
    ]
]

To clarify that the same variable and function labels in each object above actually exist and operate totally within a self-contained "context" in each object, look at the result of these lines:

money/bank
money/var
place/bank
place/var

As you can see, objects can be thought of as code containers which "encapsulate" arbitrarily labeled pieces of code. Those labels are isolated in their own "namespace" inside an object, so that they are not accidentally used, overwritten, or duplicated elsewhere in a program.

2.11.2 Inheritance

Another important characteristic of objects is that they can be copied and changed to perform specialized work. Here's an example of a new object blueprint, meant to store user account information. It contains several variable labels set to 'none, and 2 function definitions labeled 'email-address and 'display:

REBOL []
account: make object! [
    first-name: last-name: address: phone: none
    email-address: func [] [
        to-email rejoin [
            first-name "_" last-name "@website.com"
        ]
    ]
    display: func [] [
        print ""
        print rejoin ["Name:     " first-name " " last-name]
        print rejoin ["Address:  " address]
        print rejoin ["Phone:    " phone]
        print rejoin ["Email:    " email-address]
        print ""
    ]
]

Note that the variable label "email-address" is initially assigned to a function, which builds a default email address by concatenating the object's first-name and last-name variables with "@website.com". You can choose to use either that "inherited" default email definition, or "override" it by assigning an actual specific email address value. If you override the default value of the 'email-address label, the 'email-address function no longer exists in that particular object - it is overwritten by the specified email value. Below are some "instances" of the above object. Each instance creates a new labeled instantiation of the 'account object definition above, filled with default and/or specified data values:

Dave: make account []

John: make account [
    first-name: "John"
    last-name: "Smith"
    phone:  "555-4321"
]

Bob: make account [
    first-name: "Bob"
    last-name: "Jones"
    address: "4321 Street Place Cityville, USA 54321"
    phone:  "555-1234"
    email-address: "bob@mysite.net"
]

You can print out all the data contained in each object, by running each object's 'display function. Pay special attention to the 'email-address value in each object:

Dave/display
John/display
Bob/display

To understand a bit more about inheritance, take a look through the code of the 'btn object in 'svv. You'll see that it's basically just a big collection of variable labels, functions, and other object definitions:

editor svv/vid-styles/btn

Here's a red button object built from the generic svv/vid-styles/btn object above. Everything about this button is exactly the same as the generic 'btn object in 'svv, except that the 'color property has been changed to red:

REBOL []
redbtn: make svv/vid-styles/btn [color: red]
editor redbtn

Here's some example code which creates the 'redbtn object above, adds it to 'svv (along with a 'redbtn label), and then uses the new 'redbtn object in a window layout:

REBOL []
append svv/vid-styles 'redbtn
redbtn: make svv/vid-styles/btn [color: red]
append svv/vid-styles redbtn
view layout [redbtn]

Because of this "inheritance" characteristic of objects, they can be easily copied and redefined slightly so that only one function or variable, for example, is altered or added, but all the other parts remain the same. By inheriting all the functions and variables from the red button above, you could, for example, create another new tan button object which always performs one additional action, not found in the original red button definition. All you need to do is take the basic blueprint of the red button object, override the color value and extend its definition by adding the new function to it, using the same code format as above:

tanbtn: make svv/vid-styles/redbtn [
    color: tan
    size: 400x50
    action: func [] [alert "I've been clicked"]
]

Here's an example program which creates both the 'redbtn and 'bluebtn objects above, adds them to 'svv, and then uses them in a window layout:

REBOL []

append svv/vid-styles 'redbtn
redbtn: make svv/vid-styles/btn [
    color: red
    size: 200x50
]
append svv/vid-styles redbtn

append svv/vid-styles 'tanbtn
tanbtn: make svv/vid-styles/redbtn [
    color: tan
    size: 400x50
    action: func [] [alert "I've been clicked"]
]
append svv/vid-styles tanbtn

view layout [
    redbtn "Click me and nothing happens"
    redbtn "I'm just another red button object"
    tanbtn "Click me and my default message appears"
    tanbtn "I'm just another tan button object"
]

The tan button object can be used anywhere an original 'btn object from 'svv can be used. The new color, size, and action (the alert message) just exist automatically as properties of each new tan button.

An important point about this whole object creation and inheritance process is that the code which creates the 'redbtn and 'tanbtn objects could be saved in a separate file named, for example, newbtns.r:

REBOL []
append svv/vid-styles 'redbtn
redbtn: make svv/vid-styles/btn [
    color: red
    size: 200x50
]
append svv/vid-styles redbtn
append svv/vid-styles 'tanbtn
tanbtn: make svv/vid-styles/redbtn [
    color: tan
    size: 400x50
    action: func [] [alert "I've been clicked"]
]
append svv/vid-styles tanbtn

Then, anyone who wants to use the red and tan button objects can simply import and use the definitions like this:

REBOL []
do %newbtns.r
view layout [
    redbtn "Click me and nothing happens"
    redbtn "I'm just another red button object"
    tanbtn "Click me and my default message appears"
    tanbtn "I'm just another tan button object"
]

If the 'redbtn and 'tanbtn object specifications become more complex in the future, the work of writing their code can be broken up into even smaller chunks. The code for the 'redbtn object could be saved in yet another separate file, worked on by a separate team of developers, and simply imported by those who need to inherit its properties. The tan button object always inherits all the existing functionality of the red button (which in turn inherits from the built-in generic 'btn object in 'svv, which in turn is derived from lower level GUI objects, which are in turn created using graphic draw commands, event management functions, etc.). Any of the 'btn, 'redbtn, and 'tanbtn objects can be used as a basis for designing more new button objects with specialized features.

2.11.3 Why is this Useful?

The encapsulation and inheritance properties of objects allow developers to separate the development of portions of programs, so they can be worked on by different groups of coders. Each group only needs to work on making their objects perform according to a given specification (i.e., each object needs to contain a certain set of available functions, parameters, and variables which operate as expected). In this way, objects work like little black boxes, or generic building blocks, which just do what they're supposed to do, and which can be assembled as needed to form larger parts of programs. You could potentially create objects, for example, which perform all the work required of cells in a spreadsheet program, characters in a game, ports in a network application, etc. Developers of each object can make changes and improvements to the way their object operates, without affecting the way users of the object need to interact with it in code, as long as the specifications of the object (the names of functions, parameters, and variables it contains) stay the same. The definition of the functions, parameters, and variables, such as the color, look, and computations performed by a GUI button, or the function used to compute an account property, however, can be improved, without other developers having to change any code in their part of the larger program. This allows for more modular and simplified development by both users and designers of the objects in any program.

3. Rebol Dialects and Other Features

Rebol has a special built-in ability to create "dialects" to SIMPLIFY just about any sort of coding activity. For example, the code you saw earlier to create a little GUI layout is a Rebol dialect. It makes use of a specially designed syntax which hides most of the complexity of the objects, drawing routines, event management functions (actions that occur when a mouse is clicked, when a screen is closed, etc.), in an easy to read/write mini-language designed specifically for creating GUIs. You don't really need to understand anything about the object and function structure in Rebol's 'svv object to create useful screen displays. The GUI dialect parser converts simple layout code into complex lower level code comprised of objects and other more complex code structures. For example, one feature of the GUI dialect lets users create new widget objects using the 'style word. In Rebol's GUI dialect, here's how you create and use a 400x50 pixel red button which always alerts the user that it's been clicked (absolutely no formal understanding of objects is required to use this code):

REBOL []
view layout [
    style redbtn btn red 400x50 [alert "clicked"]
    redbtn "button 1"
    redbtn "button 2"
]

That code is really as concise as could possibly be imagined - much simpler than the mess of object definitions you saw in the previous section. Internally, the dialect code above is converted into a more complex lower level object, function, and variable constructs, which the user of the dialect never needs to see.

Rebol was designed with dialect creation in mind as a fundamental approach to writing code. It provides a 'parse function and a set of key language features which make it simple to create dialects that reduce code complexity to the lowest possible levels of simplicity. Designers of dialects can create specialized code structures which translate into much more complex internal operations under the hood, and perform enormous amounts of computing activity, using the shortest, easiest to use, highest level syntax possible. That's one of the main reasons why Rebol code always seems to be so dramatically short and simple, compared to examples in other languages. The built in 'parse function allows for the creation of complete mini languages which drastically reduce the complexity of coding syntax patterns needed to solve domain specific problems. Dialects have been created to provide solutions to a variety of common programming situations and routines, and Rebol is ripe for creating dialects related to any new problems which may come in the future.

Other Rebol features include:

  1. The ubiquitous series approach used to manage lists of all types: files, strings, email accounts, data in network ports, etc., all using the same functions and way of thinking.
  2. Simple data persistence features such as read, write, save, and load which, along with all the standard series functions, eliminate the need for 3rd party database systems.
  3. Lots of built in data types which are automatically recognized and handled appropriately. Not just numbers and text, but dates, times, money values, coordinates, IP addresses, etc. can all be calculated, sorted, evaluated, etc. without any special formatting. This recognition of complex data types is also important in designing dialect rules.
  4. A complete lack of all unnecessary syntax (semicolons, commas, indentation, quotes, etc.), so that code can read much more naturally, simply, and without clutter.
  5. Native ability to draw graphics, display images, and build GUI elements with events, timers, etc.
  6. Native access to network ports, files, and other data sources.
  7. Native access to Internet protocols such as HTTP, FTP, email, etc.
  8. Native ability to parse formats such as .csv, HTML, XML, etc.
  9. Native access to database systems.
  10. Natural, native interactivity between all these fundamental computing capabilities, and the ability to create new data types (such as JSON, PDF, Flash, etc.), and put them to use with simple series functions and dialects that are constructed using syntax patterns which are as simple and straightforward as possible.

All this may sound complex. Functions, Objects, Series, Data Persistence, Conditional Expressions, Dialects... But, it's not. Rebol makes all these concepts immediately accessible and usable. Once you understand some simple basics about syntax, you can jump right into learning code by rote. Follow the next couple sections and you'll start to see how to create actual usable apps in Rebol, very quickly, using functions, variable labels, lists, loops, conditional evaluations, user interfaces, and other basics processes such as concatenation. It's really very easy. In about a half hour, you'll understand how to create more than 20 interesting programs of all kinds.

4. Some Practical GUI Dialect, File, and List Examples in Rebol

Create a window with a button:

rebol []
view layout [
    btn "Click Me"
]

When the button is clicked, do something:

rebol []
view layout [
    btn "Click Me" [alert "Hello World"]
]

A window with a button and a text entry field labeled 'f:

rebol []
view layout [
    btn "Click Me"
    f: field
]

When the button is clicked, alert the user with the text currently in the 'f field. Try typing something in the field, then click the button:

rebol []
view layout [
    f: field "Type here, then click the button"
    btn "Click Me" [alert f/text]
]

When the button is clicked, write the contents of the field to the file mytext.txt, then alert the user that the file has been saved:

rebol []
view layout [
    f: field "Type here, then click the button"
    btn "Click Me" [
        write %mytext.txt f/text
        alert "Saved"
    ]
]

Add another button to read the file contents back into the field. Now you can close the program, run it again, and retrieve the saved text. Note that any time you change anything in a GUI, you must update the display with the 'show function:

rebol []
view layout [
    f: field 
    btn "Save" [
        write %mytext.txt f/text
        alert "Saved"
    ]
    btn "Load" [
        f/text: read %mytext.txt
        show f
    ]
]

The same exact program, except with a text area widget, instead of a one-line field. A simple text editor:

rebol []
view layout [
    a: area
    btn "Save" [
        write %mytext.txt a/text
        alert "Saved"
    ]
    btn "Load" [
        a/text: read %mytext.txt
        show a
    ]
]

Here the text is appended to mylist.txt. Instead of overwriting the file contents each time, each new write operation adds an additional line to the file. The 'rejoin function joins together the text and a newline (carriage return), so that each entry appears on a different line in the saved file:

rebol []
view layout [
    f: field "Enter some lines here..."
    btn "Save" [
        write/append %mylist.txt rejoin [f/text newline]
        alert "Saved"
    ]
    a: area "The contents of the file will appear here when loaded..."
    btn "Load" [
        a/text: read %mylist.txt
        show a
    ]
]

Display a list in a GUI layout, with a 'text-list widget:

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
view layout [
    text-list data mylist
]

Create a blank list using "copy []". Add items to a list using 'append - this creates a new list containing just the names from the original list:

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names
]

The clicked item in a text list item is referred to with "/picked":

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names [alert t/picked]
]

The "index" is the numerical location of an item in a list:

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names [alert form index? find mylist t/picked]
]

Select the next item in a list (in this case, the number for the name selected). In the example list below, each number value is always found at the next index location after the name (i.e., "Jane" is at position 5 in the example list, her number 2386 is at position 6). In code, that's thought of as 1 + (Jane's position):

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names [
        alert form pick mylist (1 + index? find mylist t/picked)
    ]
]

4.1 Building a Basic CRUD (create, read, update, delete) Contacts App

Here's the same idea as above, but with more data fields for each name. Name, Address, and Phone values can be found at Name, Name + 1, and Name + 2 positions:

rebol []
mycontacts: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]
names: copy []
foreach [name address phone] mycontacts [append names name]
view layout [
    t: text-list data names [
        alert rejoin [
            "Name: "
            t/picked
            " Address: "
            pick mycontacts (1 + index? find mycontacts t/picked)
            " Phone: "
            pick mycontacts (2 + index? find mycontacts t/picked)
        ]
    ]
]

Instead of alerting some rejoined text, display each data item in a text entry field:

rebol []
mycontacts: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]
names: copy []
foreach [name address phone] mycontacts [append names name]
view g: layout [
    t: text-list data names [
        n/text: t/picked
        a/text: pick mycontacts (1 + index? find mycontacts t/picked)
        p/text: pick mycontacts (2 + index? find mycontacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
]

Add items to a block (data displayed in a text-list) by entering text into field widgets:

rebol []
contacts: copy []
names: copy []
foreach [name address phone] contacts [append names name]
view g: layout [
    t: text-list data [] [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        names: copy []
        foreach [name address phone] contacts [append names name]
        t/data: copy names
        show t
    ]
]

Use a function to encapsulate duplicated code (this doesn't shorten the whole program much here, but if the duplicated code was, for example, 20 lines long and used in 5 different places, the whole program would be about 100 lines shorter):

rebol []
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
contacts: copy []
view g: layout [
    t: text-list data [] [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names
        show t
    ]
]

Load a list from file using 'load , and assign it a variable label. Save a list to file using 'save . Create an empty file using write/append "" (if the file already exists, nothing gets written):

rebol [title: "Contacts"]
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
extract-names
view g: layout [
    t: text-list data names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    across
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names
        show t
    ]
    btn "Save" [save %contacts contacts]
]

Notice that the title "Contacts" was added to the program header above, which is displayed in the title bar of the GUI window.

You could create a wide variety of basic "CRUD" (create, read, update, delete) apps using variations of the code above. So far, this program actually only creates and reads, but we'll add more to it shortly.

5. A Few More Complete App Examples

5.1 Tip Calculator

This app calculates the total amount, including tip, to pay for a restaurant meal:

  1. A window layout is created. It contains 3 text field widgets, labeled 'f,'t, and 'x.
  2. The 'f and 't fields contain some default money and tip rate values ($9 and .2 (20 percent)).
  3. When a value is entered into the 't field by the user, the 'x field's text is set to the value computed by multiplying the 'f field's money value, times the 't field's decimal value + 1 (i.e., the total to pay for a $9 bill is ($9 times 1.2)). The display of the 'x widget is updated with the 'show function:
REBOL [title: "Tip Calculator"]
view layout [
    f: field "$9"
    t: field ".2" [
        x/text: (to-money f/text) * (1 + (to-decimal t/text))
        show x
    ]
    x: field
]

5.2 Days Between 2 Dates

This app displays the number of days between any 2 selected dates:

  1. A window layout is created. It contains 2 buttons, a text widget displaying the text "Days Between:", and a text entry field labeled 'f.
  2. When the first button is clicked, it's face text is set to a date requested from the user. The selected date value is also assigned the variable label 's.
  3. When the second button is clicked, it's face text is set to another date requested from the user. That value is also assigned the variable label 'e. The text of the 'f field is then set to the difference between the 2 dates (e - s), and the display is updated with the 'show function.
REBOL [title: "Days Between"]
view layout [
    btn 200 "Start" [face/text: s: request-date]
    btn 200 "End" [
        face/text: e: request-date
        f/text: e - s
        show f
    ]
    text "Days Between:"
    f: field
]

5.3 Image Effects

This app allows users to apply selected effects to an image:

  1. A window layout is created. It contains an image widget labeled 'pic, and a text-list.
  2. The data in the text list is a variety of built-in effects which Rebol natively knows how to apply to images.
  3. When a value is selected from the text list, the /effect property of the 'pic image is set to that selected value, and the image is updated with the 'show function.
REBOL [title: "Image Effects"]
view layout [
    pic: image load http://re-bol.com/palms.jpg
    text-list data [
        "Invert" "Grayscale" "Emboss" "Blur" "Sharpen" "Flip 1x1"
        "Rotate 90" "Tint 83" "Contrast 66" "Luma 150" "None"
    ][
        pic/effect: to-block value
        show pic
    ]
]

5.4 Send Emails

This program allows users to send email messages.

  1. The program starts out by setting 5 required system variables: the user's POP and SMTP mail server settings, account username and password, and email address. These values must be edited to contain proper email account settings, in order for emails to actually be sent by the program.
  2. A window layout is created. It contains a text entry field labeled 'f, a multiline text area labeled 'a, a "Send" button, and 2 text widgets to tell the user what to type in the field and the area widgets ("Email:" and "Message:").
  3. Users type in the recipient's email addres, and a message to send. When the "Send" button is clicked, the 'send function is executed. The parameters of the send function are the email address gotten from the text typed into the 'f field, and the multiline text typed into the 'a area. After the email has been sent, the user is alerted with the text "Sent".
REBOL [title: "Send Email"]
system/schemes/pop/host: "pop.server1" 
system/schemes/default/host: "smtp.server1" 
system/schemes/default/user: "username"
system/schemes/default/pass: "password1" 
system/user/email: you@site.com
view layout [
    text "Email:"
    f: field "joe@site.com" 
    text "Message:"
    a: area
    btn "Send" [
        send to-email f/text a/text
        alert "Sent"
    ]
]

Try editing this program so that it allows the user to set the default email settings without having to edit any program code (you can find a few such working examples at http://re-bol.com/examples.txt)

5.5 Tile Game

The code below creates a playable tile game. Click any tile piece to move it into the empty space. Rearrange the tiles in ascending order:

  1. A GUI window layout is created.
  2. The 'style word, in Rebol's GUI dialect, is used to create a new red 'box object labeled 't. Whenever a 't widget is clicked by the user, three actions occur: the label 'x is set to the box's current 'offset (coordinate position), the box's coordinate position is set to the 'e widget's position, and the 'e widget's position is changed to the 'x position. These three actions together effectively swap the positions of the clicked box and the 'e widget (as you'll see below, the widget labeled 'e is just a plain box widget, which appears as a blank space in the window layout).
  3. The 'across word, in Rebol's GUI dialect, is used to place each consecutive widget in the window across the screen (instead of one below another, each on new lines, which is the default behavior you've seen so far in GUI layouts).
  4. The 'return word, in Rebol's GUI dialect, is used to start a new "line" of widgets (similar to how a carriage return is used to start a new line in a text document).
  5. A bunch of 't box widgets are added to lines in the window layout, along with a final box widget labeled 'e. Any time you click a red box, its position is swapped with the empty box.
REBOL [title: "Tile Game"] 
view layout [ 
     style t box red [
         x: face/offset
         face/offset: e/offset 
         e/offset: x
     ] 
     across 
     t "8"  t "7"  t "6"  return 
     t "5"  t "4"  t "3"  return 
     t "2"  t" 1"  e: box 
]

5.6 Generic Calculator

Below is a calculator app. Most of the code should make sense.

  1. A GUI window is created.
  2. The 'across word is used to place each consecutive widget in the window across the screen.
  3. The window layout includes a field labeled 'f. It is 225x50 pixels in size, and the font size of text displayed in it is 25.
  4. The 'style word is used to create a new button object called 'b, which is 50x50 pixels in size, and which appends the current button's face text to the 'f field widget's text, whenever one of the 'b buttons is clicked.
  5. A bunch of 'b buttons are added to the window layout, each displaying a different number or mathematical operator. The 'return word is used to start new lines of buttons.
  6. The "=" button attempts to set the 'f field's text to whatever results in "do"ing (evaluating) the current mathematical expression displayed in the 'f field.
REBOL [title: "Calculator"]
view layout [ 
     across 
     f: field 225x50 font-size 25  return 
     style b btn 50x50 [append f/text face/text show f] 
     b "1"  b "2"  b "3"  b " + "  return 
     b "4"  b "5"  b "6"  b " - "  return 
     b "7"  b "8"  b "9"  b " * "  return 
     b "0"  b "."  b " / "  b "=" [ 
         attempt [f/text: form do f/text show f] 
     ] 
]

Try building this same app in any other programming language, and you'll see that there is absolutely nothing simpler than Rebol's dialect approach to building windowed programs. The idea of using derived GUI widget objects, variable labels, series functions, and evaluated expressions, however, is universal to creating a similar calculator application using any programming language/tool.

5.7 Coin Flip

Clicking the button in this app randomly flips between a heads and tails coin image:

  1. Two images are loaded from specified URLs on the Internet. The label 'h is assigned to the loaded heads image. The label 't is assigned to the loaded tails image.
  2. The 'random function with the /seed refinement is used to initiate Rebol's random number generator, using the current time (a generally unique value), so that unique random values can be generated with the 'random function. Any time you want to generate random numbers, text, or other random values, this stock line of code should be included in your program.
  3. A window layout is created and labeled 'g. It contains an image widget labeled 'i which initially displays the 'h image (the coin head image), a text field entry widget labeled 'f, and a button displaying the text "Flip".
  4. When the button is clicked, it sets the 'f field text to the first item in a randomly ordered list of the 2 text strings "Heads" and "Tails".
  5. Next, an 'either conditional evaluation is performed. If the text in the 'f field is "Heads" (that random state was determined in the previous step), then the image value of the 'i widget is set to the 'h image (the heads image). Otherwise (i.e., if the text in the 'f field is "Tails"), then the image in the 'i widget is set to 't (the tails image).
  6. The display (labeled 'g) is updated using the 'show function.
REBOL [title: "Coin Flip"]
h: load http://re-bol.com/heads.jpg
t: load http://re-bol.com/tails.jpg
random/seed now
view g: layout [
    i: image h
    f: field
    btn "Flip" [
        f/text: first random ["Heads" "Tails"]
        either f/text = "Heads" [i/image: h] [i/image: t] 
        show g
    ]
]

5.8 Additional Example Apps to Study

Here are a few more short app examples. See if you can follow the basic flow of code in each program. Pay attention to which pieces of code are variable labels, GUI layout widgets, conditional evaluations, etc.

A web page editor:

REBOL [title: "Web Page Editor"]
view layout [
    f: field 600 "ftp://user:pass@site.com/public_html/page.html"
    a: area 600x350 
    across 
    btn "Load" [a/text: read to-url f/text  show a]
    btn "Save" [write to-url f/text a/text  alert "Saved"]
]

Here's an app to quiz users on basic addition math facts. Try changing it to quiz subtraction, multiplication, and division on selected ranges of random numbers (i.e., subtraction on numbers 1 - 100 or multiplication on numbers 1 - 12):

REBOL [title: "Math Test"]
random/seed now
x: func [] [rejoin [random 10 " + " random 20]]
view layout [
    f1: field x
    text "Answer:"
    f2: field [
        alert either f2/text = form do f1/text ["Yes!"]["No"]
        f1/text: x  show f1  focus f2
    ]
]

Try changing this one to generate insults:

REBOL [title: "Compliment Generator"]
random/seed now
view layout [
    x: area "brilliant rare unique talented exceptional"
    y: area "genius champion winner success achiever"
    btn "Compliment" [
        alert rejoin [
            "You're a "
            first random parse x/text none " "
            first random parse y/text none "!"
        ]
    ] 
]

A useful tool for web developers, which allows you to select and edit files in a folder on your FTP server:

REBOL [title: "FTP Tool"]
view  layout [
    f: field 600 "ftp://user:pass@site.com/public_html/" [
        either dir? to-url value [
            t/data: sort read to-url value 
            show t
        ][
            editor to-url value
        ]
    ]
    t: text-list 600x400 [editor to-url join f/text value]
]

6. Graphics

6.1 Rebol "Draw" Basics

To draw graphics, put a box widget into a GUI layout, followed by an 'effect block containing 1 or more 'draw commands:

rebol []
view layout [
    box 400x400 effect [
        draw [
            line 10x39 322x211
        ]
    ]
]

The draw commands are contained inside a BLOCK:

rebol []
view layout [
    box 400x400 black effect [
        draw [
            line 0x400 400x50
            circle 250x250 100
            box 100x20 300x380
            curve 50x50 300x50 50x300 300x300
            spline closed 3 20x20 200x70 150x200
            polygon 20x20 200x70 150x200 50x300
        ]
    ]
]

So you could, for example, use a foreach loop to add items to the screen. We'll append draw commands to the /effect/draw block of the object labeled 'b (in this case, the box widget being drawn upon):

rebol [title: "Drawing Animation"]
shapes: [
    [line 0x400 400x50]
    [circle 250x250 100]
    [box 100x20 300x380]
    [curve 50x50 300x50 50x300 300x300]
    [spline closed 3 20x20 200x70 150x200]
    [polygon 20x20 200x70 150x200 50x300]
]
view layout [
    b: box 400x400 black effect [draw []]
    btn "Animate Drawing" [
        foreach g shapes [
            append b/effect/draw g
            show b
            wait 1
        ]
    ]
]

Or use widgets to make changes to the data in the draw block. This code changes the 'x and 'y values of the 'pos variable (the circle's position), then updates the 'b display, using the 'show function:

rebol [title: "Animated Circle"]
pos: 200x200
view layout [
    b: box 400x400 black effect [
        draw [circle pos 20]
    ]
    across
    btn "Up"    [pos/y: pos/y - 10 show b]
    btn "Down"  [pos/y: pos/y + 10 show b]
    btn "Right" [pos/x: pos/x + 10 show b]
    btn "Left"  [pos/x: pos/x - 10 show b]
]

Here's the same example again using cursor key controls:

rebol [title: "Keyboard Controlled Animated Circle"]
pos: 200x200
view layout [
    b: box 400x400 black effect [
        draw [circle pos 20]
    ]
    across
    key keycode [up]    [pos/y: pos/y - 10 show b]
    key keycode [down]  [pos/y: pos/y + 10 show b]
    key keycode [right] [pos/x: pos/x + 10 show b]
    key keycode [left]  [pos/x: pos/x - 10 show b]
]

Color can be added to shapes in the draw block using the "pen" command. Shapes can be filled with color, with images, and with other graphic elements using the "fill-pen" command. The thickness of drawn lines is set with the "line-width" command:

rebol []
view layout [
    box 400x400 black effect [
        draw [
            pen red
            line 0x400 400x50
            pen white
            box 100x20 300x380
            fill-pen green
            circle 250x250 100
            pen blue
            fill-pen orange
            line-width 5
            spline closed 3 20x20 200x70 150x200
            polygon 20x20 200x70 150x200 50x300
        ]
    ]
]

6.2 A Few Short Paint Apps

Making a paint program is as simple as adding items to a draw block, using any sort of controlled input from a user:

rebol [title: "Paint Lines"]
view layout [
    b: box black 400x400 effect [draw []]
    text "line start point:"
    f1: field "0x400"
    text "line end point:"
    f2: field "400x50"
    btn "Add line" [
        append b/effect/draw compose [
            line (to-pair f1/text) (to-pair f2/text)
        ]
        show b
    ]
]

Here's a colorful paint example that uses key controls:

rebol [title: "Colorful Etch a Painting"]
pos: 200x200
paint: func [] [
    append b/effect/draw compose [
        pen (random 255.255.255)
        circle (pos) 2
    ]
    show b
]
view layout [
    b: box 400x400 black effect [
        draw [circle pos 10]
    ]
    across
    key keycode [up]    [pos/y: pos/y - 4  paint]
    key keycode [down]  [pos/y: pos/y + 4  paint]
    key keycode [right] [pos/x: pos/x + 4  paint]
    key keycode [left]  [pos/x: pos/x - 4  paint]
]

As you can see, drawing graphics in Rebol is all about managing that draw block by using series functions, adjusting variable values, etc. In that way, graphics programming is a lot like creating CRUD apps similar to the contacts example you saw earlier.

7. More About Series - Adding More Features to the Contacts App

Learning how to do anything more in Rebol requires understanding more about how to use blocks ("series") to store and manipulate data. Let's take the contacts example from earlier:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
]
extract-names
view g: layout [
    t: text-list data names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        extract-names
        t/data: copy names
        show t
    ]
    btn "Save" [save %contacts contacts]
]

If you want to remove a contact, you'll need a function to remove 3 data items from the saved list (name, address, and phone). Let's add a button which does that - it uses the 'remove/part function:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
view g: layout [
    t: text-list data extract-names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    across
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names
        show t
    ]
    btn "Remove" [
        remove/part (find contacts t/picked) 3
        t/data: extract-names
        show t
    ]
    btn "Save" [save %contacts contacts]
]

Now let's add a button to sort the entries by name. You can use the 'sort/skip function/refinement to perform that operation:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
view g: layout [
    t: text-list data extract-names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    across
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names  show t
    ]
    btn "Remove" [
        remove/part (find contacts t/picked) 3
        t/data: extract-names  show t
    ]
    btn "Sort" [
        sort/skip contacts 3
        t/data: extract-names  show t
    ]
    btn "Save" [save %contacts contacts]
]

We can sort in the opposite order using the 'sort function with the /skip and /reverse refinements. Notice that in each one of our new buttons, we're using the extract-names function. If we hadn't created that function, there certainly would have been a lot of duplicate code in this program:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
view g: layout [
    t: text-list data extract-names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn 100 "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names  show t
    ]
    btn 100 "Remove" [
        remove/part (find contacts t/picked) 3
        t/data: extract-names  show t
    ]
    btn 100 "Sort Ascending" [
        sort/skip contacts 3
        t/data: extract-names  show t
    ]
    btn 100 "Sort Descending" [
        sort/skip/reverse contacts 3
        t/data: extract-names  show t
    ]
    btn 100 "Save" [save %contacts contacts]
]

7.1 Built in Help

So where do you learn about all these useful series operations? Well, Rebol can provide information about using any function, with the 'help function, and you can get a list of all available functions with the 'what function. To get a start, try running this script:

REBOL []
help pick
help find
help at
help index?
help length?
help append
help remove
help insert
help extract
help copy
help replace
help select
help sort
help reverse
help head
help next
help back
help last
help tail
help skip
help change
help poke
help clear
help join
help intersect
help difference
help exclude
help union
help unique
help empty?
help write
help read
help save
help load
halt

Rebolers use the 'help function regularly when writing code. Information about using Rebol's built in GUI dialect (called "VID") can be found by examining the objects in SVV:

REBOL []
? svv
probe extract svv/vid-styles 2
? svv/vid-styles/button
? svv/vid-words
? svv/facet-words

Run the program below to create a Rebol word reference manual. The results are saved in a file named help.txt, and displayed using Rebol's built in text editor. Using the editor, you can rename the file and save it anywhere on your hard drive, make notes and write in your own examples, etc., for future reference:

REBOL [Title: "Quick Manual"]
print "This will take a minute..."  wait 2
echo %words.txt what echo off   ; "echo" saves console activity to a file
echo %help.txt
foreach line read/lines %words.txt [
    word: first to-block line
    print "___________________________________________________________^/"
    print rejoin ["word:  " uppercase to-string word]  print "" 
    do compose [help (to-word word)]
]
echo off
x: read %help.txt
write %help.txt "VID STYLES (GUI WIDGETS):^/^/"
foreach i extract svv/vid-styles 2 [write/append %help.txt join i newline ]
write/append %help.txt "^/^/LAYOUT WORDS:^/^/" 
foreach i svv/vid-words [write/append %help.txt join i newline]
b: copy [] 
foreach i svv/facet-words [
    if (not function? :i) [append b join to-string i "^/"]
]
write/append %help.txt rejoin [
    "^/^/STYLE FACETS (ATTRIBUTES):^/^/" b "^/^/SPECIAL STYLE FACETS:^/^/"
]
y: copy ""
foreach i (extract svv/vid-styles 2) [
    z: select svv/vid-styles i
    ; additional facets are held in a "words" block:
    if z/words [
        append y join i ": "
        foreach q z/words [if not (function? :q) [append y join q " "]]
        append y newline
    ]
]
write/append %help.txt rejoin [
    y "^/^/CORE FUNCTIONS:^/^/" at x 4
]
editor %help.txt

7.1.1 The Word-Broswer.r Script

The program below presents nicely formatted help text with clickable examples that clarify how all Rebol's functions work. It covers the overwhelming majority of the Rebol language, and everything is organized into helpful categories, and cross referenced with other related functions, so you can quickly find and use the code you need. It's an indispensable tool which will help you learn all about the core language functions:

do http://re-bol.com/wordbrowser.r

8. A Few More Short Apps Using Series and Loop Constructs

Some of examples in this section may get a bit complicated if you're just starting out with Rebol. Try not to get stuck. Read through everything and absorb as much as you can. Then go back and read the whole thing again. If you're interested in pursuing coding with Rebol, just continue to digest a few more tutorials (and books, examples on rebol.org, etc.) and it will all start to sink in quickly. You'll see most of these code patterns and concepts repeated over and over again in all sorts of examples.

8.1 Image Slideshow

This app displays all the images in the current folder, with a 3 second pause between each:

  1. The list of files in the current directory is read, and assigned the variable label 'files.
  2. A 'forever loop is started.
  3. Inside each repeat of the 'forever loop, a 'foreach loop is executed. For each file in the folder, if the suffix of the file is found in the given list of file extensions (png, jpg, gif, bmp), then any currently displayed window layout is unviewed, and a new window layout displaying the current file image is displayed. If the user clicks the image with the mouse, the program is quit. Otherwise the program waits 3 seconds and then continues with the loops.
REBOL [title: "Image Slideshow"]
files: read %./
forever [
    foreach file files [
        if find [%.png %.jpg %.gif %.bmp] suffix? file [
            unview 
            view/new center-face layout [
                image file [quit]
            ]
            wait 3
        ]
    ]
]

8.2 Bar Chart

This little app demonstrates how to visually display a list of numerical values in a graphic bar chart:

  1. In this example, a block (list) of months and numerical values is created, labeled 'd.
  2. An empty block labeled 'gui is created.
  3. A 'foreach loop is used to go through each month and value pair in the 'd list. For each pair of month and value items in the 'd list, some GUI layout code is appended to the 'gui block. The appended code consists of a button, sized according to the computation (value * 10), and displaying the month text. The 'compose function is used to evaluate the parenthesized size and month values. For example, if the selected month and value items are "March" and 13, the items added to the 'gui block are [btn 130 "March"].
  4. The constructed 'gui layout block is displayed. The 'center-face word is used to center the window layout on screen.
REBOL [title: "Bar Chart"] 
d: ["March" 13 "April" 9 "May" 21 "June" 29 "July" 10] 
gui: copy []
foreach [month value] d [
    append gui compose [btn (value * 10) (month)]
]
view center-face layout gui

8.3 Paypal Report

This example downloads a Paypal account file from the web, and computes a sum of all sales in the "Gross Sales" column:

  1. A variable label 'sum is set to represent an initial value of $0.
  2. A variable label 'x is set to represent a block of line data read from the given URL.
  3. The first line in the 'x data block is a bunch of column headers (titles for each column of data). We don't want to use that line in our computations, so a new variable 'y is set to represent all but the first line of 'x values (i.e., 'y is set to start "at" the second line in 'x).
  4. A foreach loop is started, to run through each 'line in the 'y block.
  5. The data on each line of the Paypal file is in "CSV" or "comma separated value" format, which means that each value on each line of the file is separated by a comma character. The 'parse function separates each line into a block of values, labeled 'values here.
  6. The "Gross Sales" value is found in column number 8 on each line, so we'll add the current 'sum value to the money value picked from item 8 in the 'values block. By doing this for each line, the final 'sum value will be the total of all individual Gross Sales values.
  7. Alert the sum to the user.
REBOL [title: "Paypal Reports"]    
sum: $0
x: read/lines http://re-bol.com/Download.csv
y: at x 2
foreach line y [
    values: parse/all line ","
    sum: sum + to-money pick values 8
]
alert join "Total Gross Sales: " sum

8.4 Tric Trac Game

The point of the Tric-Trac board game is to roll two dice, and match the rolled number with any combination of available numbers on the board. For example, if you roll a 6 with the dice, you can match with the numbers 5 + 1, or 4 + 2, or 3 + 2 + 1, etc., on the board. Once a number on the board has been selected, it cannot be used again for the rest of the game. The game is over when you have no possible combinations of number buttons to match the rolled value. To determine your final score, sum the remaining unselected numbers on the board. The goal is to get the lowest final score (you can play against yourself or take turns playing against others). Here's how the code works in this simple Tric-Trac game simulation:

  1. The "random/seed" function is used to seed Rebol's random number generator with the current time, so that unique random numbers can be generated with the 'random function.
  2. A new block labeled 'g is created. To start out, that block contains 2 lines of GUI dialect code: the 'across layout word, and a button which alerts the user with a random value between 1 and 12 (to simulate a roll of the dice).
  3. A 'repeat loop is used to add GUI code to the 'g block. That code consists of a text widget displaying a number 1 - 12, and a check widget.
  4. The 'g block layout code is displayed in a centered GUI layout window.
REBOL [title: "Tric Trac"]
random/seed now
g: [
    across 
    btn "Roll" [alert form 1 + random 11]
] 
repeat i 12 [
    append g compose [text (mold i) check]
]
view center-face layout g

A version of this game with many more features is available at http://www.rebol.org/view-script.r?script=trictrac.r

8.5 Shopping List and To-Do List

This app keeps a shopping list of items to buy. It checks to make sure that you're not adding duplicate items, and allows you to save and load your list.

  1. A window layout is created. It contains five widgets: a text list 500x400 pixels in size, labeled 't, a text entry field 500 pixels wide, labeled 'f, and three buttons.
  2. When the user enters an item in the 'f field, the program checks to see if the entered text is already found in the list. If a duplicate entry is not found in the list, the item is added to the data block displayed by the 't widget (t/data). The display is updated with the 'show function, and the cursor is placed back in 'f field, with the 'focus function.
  3. If the user clicks the "Remove Selected" button, the picked item is removed from the text list, and the display is updated.
  4. If the user clicks the "Save List" button, the data in the text list is saved to the file %shopping.txt, and the user is alerted with the text "Saved".
  5. If the user clicks the "Load List" button, the text list is set to display the data loaded from the %shopping.txt file, and the display is updated.
REBOL [title: "Shopping List"]
view center-face layout [
    t: text-list 500x400
    f: field 500 "(Enter Items Here)" [
        if not find t/data f/text [
            append t/data copy f/text
        ] 
        show t 
        focus f
    ]
    across
    btn "Remove Selected" [remove find t/data t/picked  show t]
    btn "Save List" [save %shopping.txt t/data  alert "Saved"]
    btn "Load List" [t/data: load %shopping.txt  show t]
]

Here's a slightly more complicated "To-Do List" variation of the program above, which adds some features. It provides the option to save and load lists with different file names, the ability to move items up or down in the list order, and the ability to save a screenshot of the program to a .png image, and view that image in a web browser. Try to pick out the series functions and variable labels, and see if you can follow the general program flow:

REBOL [title: "To Do"]
view/new gui: layout [
    t: text-list 500x400
    across
    f: field [if not find t/data v: value [append t/data copy v] focus f]
    btn "Del"  [remove find t/data t/picked]
    btn "Up"   [attempt [move/to z: find t/data t/picked (index? z) - 1]]
    btn "Down" [attempt [move/to z: find t/data t/picked (index? z) + 1]]
    btn "Load" [attempt [t/data: load request-file/file/only %todo.txt]]
    btn "Save" [attempt [
        save request-file/save/file/only %todo.txt t/data
        alert "Saved"
    ]]
    btn "Print" [save/png %todo.png to-image gui  browse %todo.png]
] 
forever [wait .1  show gui  if not viewed? gui [quit]]

8.6 Calendar

Here's a little personal calendar app which allows you to save events for specific days:

  1. A new file "mycal" is created to save the calendar event data. Remember, the /append refinement of the 'write function adds data to a file (instead of erasing the existing contents). If the file doesn't already exist, the 'write function creates a new empty file (i.e., with nothing ("") added to it). If the file already exists, nothing ("") is added to it, so it stays the same.
  2. A 'forever loop is started.
  3. A 'request-date function is used to get a date selected by the user. The result of that function is labeled 'd. If the user selects no date ('none), then the program quits.
  4. A window layout is created and centered on screen (with the 'center-face GUI dialect word).
  5. The selected date is displayed in bold text at the top of the window.
  6. A text area widget labeled 'a is added to the layout. It contains the data "select"ed (found after) the 'd date value, in the "mycal" file (if the selected date isn't found in the mycal file, nothing is displayed).
  7. A button widget is added to the layout. When it's clicked, the date ('d) and text contained in the area labeled 'a, are saved to the mycal file, and then the current layout window is unviewed.
  8. The 'forever loop starts over.
REBOL [title: "Calendar"]    
write/append %mycal "" 
forever [ 
     if none = d: request-date [quit] 
     view center-face layout [ 
         text bold form d 
         a: area form select (l: load %mycal) d 
         btn "Save" [ 
             save %mycal head insert l reduce [d a/text] 
             unview 
         ] 
     ] 
]

8.7 Pig Latin Generator

This console program allows the user to enter a phrase, which is converted to Pig Latin:

  1. The user is asked for a string of text from the user. The ^/ characters at the end of the quoted text are used to print a carriage return (they're the same thing as a "newline"). The label 'p is assigned to the text entered by the user.
  2. The 'parse function is used to split apart strings at specified characters. Here, the phrase labeled 'p above, is split apart at each space. The result of the 'parse function is a block of text strings (here, the individual words of the phrase entered by the user). That list of words is assigned the label 's.
  3. A foreach loop is used to go through each word in the 's block of words. During each repeat of the foreach loop, the individual words from the list are labeled 'w.
  4. During each repeat of the foreach loop, each individual word is split apart at the vowels. That block of characters is labeled 'x.
  5. The Pig Latin output is created by concatenating several pieces of text: the part of the word before the first vowel, and the part of the word beginning with the first vowel. A conditional 'either evaluation is used to determine which portion of the given word should be used in each one of the Pig Latin halves of the word. Another 'either evaluation is used to determine which ending characters should be used to form the second word in the Pig Latin text (based on whether or not non-vowel characters exist before the first part of the orginal word). For example, if the word starts with a consonant letter, the second half of the Pig Latin pair ends in "ay". Otherwise, it ends in "hay". Note that the 'prin function is used to print each Pig Latin word. The 'prin function does the same thing as 'print, except it doesn't attach a newline character to the end of each printed item (in this case, the allows each of the output words to be printed next to one another, instead of on separate lines).
  6. A blank line is printed, and the 'halt function is used to keep the program from closing, so that the user can read the printed results.
REBOL [title: "Pig Latin"]
p: ask "Type some words to convert to Pig Latin:  ^/^/"
s: parse p " "
foreach w s [
    x: parse w "aeiou"
    prin rejoin [
        either q: find/match w x/1 [q] [w] 
        x/1 
        either q ["ay"]["hay"]
        " "
    ]
]
print newline
halt

8.8 Marching Alien Logo Army

Here's an example that moves an army of Rebol logo graphics across the screen:

  1. A block of starting position coordinates is defined, labeled 'poss.
  2. A 'build-block function is defined. It builds a block of draw commands from the block of existing position coordinates, moving each image 8 pixels from the existing position. In this function, the 'poss block values are first copied over to the variable label 'existing-poss, and a new blank 'poss block is created. A foreach loop is used to fill the new 'poss block with coordinates values that are moved 8 pixels to the right (+ 8x0) from their locations in the existing poss block. Then a new 'logos block is created, and a foreach loop is used to fill it with draw commands composed from the generated coordinates in the new 'poss block.
  3. The 'build-block function is run once before showing the window display, to create a 'logos block of draw commands. The function definition above just defines what the 'build-block function does. In order to run that code, you must actually execute the 'build-block function (just like any other function (request-text, now, etc.), the function is executed by just typing its name).
  4. A window layout is created. It contains a draw block displaying all the graphics commands in the 'logos block.
  5. The window layout also contains a button widget. When the user clicks the button, a block of code is looped 10 times. Inside the loop, the 'build-block function is executed, the display of the 'b box is updated with the 'show function, so that it displays the new graphics commands in the generated 'logos block, with the images shifted over 8 pixels to the right. The loop is paused for .2 seconds during each of the 10 iterations.
REBOL [title: "Marching Alien Logo Army"]
poss: [
    10x30 120x30 230x30 340x30 450x30 560x30
    10x60 120x60 230x60 340x60 450x60 560x60 
]
build-block: func [] [
    existing-poss: copy poss
    poss: copy []
    foreach pos existing-poss [append poss pos + 8x0]
    logos: copy []
    foreach pos poss [append logos compose [image (pos) logo.gif]]
]
build-block
view layout [
    b: box 800x300 aqua effect [draw logos]
    btn "Move 10 clicks"[
        loop 10 [
            build-block
            show b
            wait .2
        ]
    ]
]

8.9 Catch Game

In this game the player tries to catch falling Rebol logo images, using cursor keys to move. Speed increases as the game progresses. Game play ends when a falling logo hits the floor. Final score is the amount of time the game was played. Scores are saved along with each player's name, sorted from lowest to highest score, and displayed at the end of the game. Take a look at the GUI window elements, conditional evaluations of computed variable values inside a forever loop, score list management operations, persistent data storage, etc.:

  1. The "random/seed" function is used to seed Rebol's random number generator with the current time, so that unique random numbers can be generated with the 'random function.
  2. The variable label 'start is set to hold the time when the program was started.
  3. The variable 'speed is set to an initial value of 2.
  4. A vindow layout is created. The /new refinement of the 'view function is used to create a window, without showing it yet. The layout is labeled 'g. The layout is 600x440 pixels in size. An image widget containing the built in 'logo.gif image is placed at coordinate 280x20. That widget is labeled 'a. A blue button widget sized 50x20 pixels is placed at coordinate 280x420. That widget is labeled 'b. Keyboard handlers for the left and right arrow keys are set up. If the left key is pressed, the position of the blue 'b button is shifted 10 pixels to the left. If the right key is pressed, the 'b button's coordinate is shifted 10 pixels to the right.
  5. A forever loop is started.
  6. The position of the 'a image is shifted down. The distance of the shift is determined by the 'speed variable. The 'as-pair function is used to create a coordinate in which the y (vertical) value is the rounded 'speed value. That incremented coordinate value is added to the current coordinate of the 'a widget. The end result of this calculation is that the widget is moved down 'speed number of pixels.
  7. A conditional 'if evaluation is used to check whether the 'a and 'b widgets are overlapping (i.e., the player piece and the falling image are touching/coliding - if so, the falling piece is being caught). If so, the position of the 'a widget is set to a random x coordinate at the top of the screen. This starts dropping a "new" logo image, once the previous falling image has been caught (there's really only one logo image in the entire game - it's just repositioned). The x value of the random coordinate is somewhere between 1-500 pixels from the left side of the window, and the y value is 20 pixels from the top of the window. The speed value is also incremented by .1 every time a falling logo is caught. This means that every 10 catches, the speed increases by one full rounded pixel value.
  8. An 'if evaluation is used to check whether the y value of the 'a widget's coordinate is greater than 440 (i.e., the image is touching the floor). If so, game play is over, so the 'break function is used to end the 'forever loop.
  9. If game play hasn't been stopped above, the program waits .01 seconds, shows all updates to the window layout (labeled 'g), and continues with another repeat of the 'forever loop.
  10. When game play ends, the score is saved, and high scores are displayed in sorted order from lowest to highest. To do this, first a "scores" file is created, if it doesn't already exist. Next, any existing scores are loaded from the file. Then the score list is appended with 2 values: the current score (which is the current time minus the start time), and the player's name (gotten using the 'request-text function). The score list is sorted by score, using the 'skip refinement (every 2 items in the list contains a score and a name (score, name, score, name, etc.)), and that sorted list is saved to the scores file. The 'request-list function is used to display all the sorted scores.
REBOL [title: "Catch Game"]
random/seed now   
start: now/time
speed: 2
view/new center-face g: layout [
    size 600x440 
    at 280x20   a: image logo.gif
    at 280x420  b: btn 50x20 blue
    key keycode [left]  [b/offset: b/offset - 10x0]
    key keycode [right] [b/offset: b/offset + 10x0]
]
forever [
    a/offset: a/offset + (as-pair 0 round speed)
    if overlap? a b [
        a/offset: as-pair (random 500) 20
        speed: speed + .1
    ]
    if a/offset/y > 440 [break]
    wait .01
    show g
]
write/append %scores ""
scores: load %scores
append scores now/time - start 
append scores request-text/title "Name:"
save %scores sort/skip scores 2 
request-list "High Scores:" scores

8.10 Guitar Chord and Scale Diagrammer

This program allows the user to quickly create and save images of guitar fretboard diagrams for chords and/or scales. When the program starts, enter the number of frets you want in your diagram (the default is 5 frets, but you could use 3 or 4 for smaller diagrams, or more for full fretboard scale diagrams). Click any fret on any string to add a dot (finger position). Click any added dot to remove it (change it back to an empty position). Right-Click any fret on any string to add a character of your choice (you could use this feature to add finger numbers, root note labels, interval labels, etc.). Click the title text ("Chord Name") to give the chord or scale a name. Right-Click the title to save the diagram to a .png image (the default image file name is the title text entered above).

  1. The program starts by getting a number of frets from the user. The 'request-text function is used, with the refinements /title and /default (a default number of 5 is presented to the user). The user's answer is labeled 'f.
  2. A block of GUI dialect code is labeled 'g. The word 'backdrop sets the background color of the window layout to white. The word 'across is used arrange the widgets across the screen. The word 'space is used to set the space between widgets to 0 in both the horizontal and vertical direction (there's usually a default space buffer between widgets - here it's set to 0x0). The word 'origin is used to set the position of the first widget in the layout to 0x0.
  3. The 'style word is use to create a new widget labeled 's, which is a 20x20 pixel box, displaying the character "|" on its face, with a font size of 20, a font color of black, and a font shadow offset of 0x0 (no shadow). There are two actions set up for the 's widget. The first block runs whenever the widget is left-clicked with the mouse (just like the normal action block you've seen in all the other examples so far). The second block runs whenever the widget is right-clicked with the mouse. When any 's widget is left-clicked, the face text is set to either "O" or "|". The 'either conditional evaluation is used to determine if the current face text is "|". If so, the face text is set to "O", if not the face text is set to "|". If any 's widget is right-clicked, the face text is set to some text requested from the user.
  4. A text widget labeled 't is added to the layout. It is 120x24 pixels in size, with bold, centered text, and a font size of 18. It is assigned 2 alternate action blocks, to react to left and right clicks of the mouse. If the text widget is left clicked, the face text is set to some text requested from the user. The 'request-text function is used, with the /default refinement, and the default text that appears in the requester is the current face text on the widget. If the text widget is right clicked, an attempt is made to save the 'p layout to an image file. The 'save function with the /png refinement takes 2 parameters: the name of the file to save to, and the data to save. In this case, the name of the file is gotten from the user, with the 'request-file function. The /save /only and /file refinements of the 'request-text function bring up a save dialogue which allows for only one file selection, with a default file name (in thise case, the text of the 't widget joined with the characters ".png"). The image data is created using the 'to-image function, with the current layout (labeled 'p) as it its parameter.
  5. The 'loop function is used to run a block of code 'f number of times. Remember, the 'f label was set at the beginning of the program - it's the number of frets to include in the layout. Here, the text answer gotten from the user needs to be converted to an integer number. That's accomplished using the 'to-integer function. The looped block appends a number of 's widgets and a 'return word (newline), to the 'g layout block above.
  6. The 'g layout code block is viewed, and that layout is labeled 'p. To be clear, the label 'g refers to the GUI dialect code block used to create the layout. The label 'p refers to the actual current layout (which internally consists of lots of low level objects, functions, draw commands, etc.). That low level, currently drawn layout is what's required as a parameter of the 'to-image function.
REBOL [title: "Guitar Chord and Scale Diagrammer"]
f: request-text/title/default "Number of frets in each diagram:" "5"
g: [
    backdrop white
    across  space 0x0  origin 0x0
    style s box 20x20 "|" font-size 20 font-color black shadow 0x0 [
        face/text: either "|" = face/text ["O"] ["|"] 
    ] [
        face/text: request-text
    ]
    t: text 120x24 bold center font-size 18 "Chord Name" [
        face/text: request-text/default face/text
    ] [ attempt [
        save/png request-file/save/only/file join t/text ".png" to-image p
    ] ]
    return
]
loop to-integer f [append g [s s s s s s return]]
view center-face p: layout g

Here's a version of the program above, with an added HTML layout feature. Press the "p" key on your keyboard to create an HTML layout (web page) of any selected diagrams you've created. The HTML layout can be printed, emailed, uploaded to a web site, etc. You'll learn more about HTML in the next section of this tutorial. Come back afterward and see if you can follow the 'layout-html function more clearly:

REBOL [title: "Guitar Chord and Scale Diagrammer"]
f: request-text/title/default "Number of frets in each diagram:" "5"
layout-html: func [] [
    filelist: sort request-file/filter/title 
        ["*.png"] "Select image(s) to print:" ""
    html: copy "<html><body>"
    foreach file filelist [
        append html rejoin [
            {<img src="file:///} to-local-file file {"> &nbsp; &nbsp; }
        ]
    ]
    append html [</body></html>]
    write %chords.html trim/auto html
    browse %chords.html 
]
g: [
    at 100x100 key #"p" [layout-html]
    backdrop white
    across  origin 0x0  space 0x0
    style s box 20x20 "|" font-size 20 font-color black shadow 0x0 [
        face/text: either "|" = face/text ["O"] ["|"] 
    ] [
        face/text: request-text
    ]
    t: text 120x24 bold center font-size 18 "Chord Name" [
        face/text: request-text/default face/text
    ] [ attempt [
        save/png request-file/save/only/file join t/text ".png" to-image p
    ] ]
    return
]
loop to-integer f [append g [s s s s s s return]]
view center-face p: layout g

The apps in this section clearly demonstrate that lists and loop code structures are important additions to concepts such as variables, functions, persistent data storage, and other basic coding principles.

8.11 Additional Series Apps to Study

Here are a few more short app examples which make use of series constructs. See if you can follow the basic flow of code in each program.

A full screen presentation (try to gues which system variable holds the size of the screen). Note that the slide layouts are stored in a nested block of blocks:

REBOL [title: "Simple Presentation"]
slides: [
    [
        at 0x0 box system/view/screen-face/size white [unview]
        at 20x20 h1 blue "Slide 1"
        box black 2000x2
        text "This slide takes up the full screen."
        text "Adding images is easy:"
        image logo.gif
        image stop.gif
        image info.gif
        image exclamation.gif
        text "Click anywhere on the screen for next slide..."
        box black 2000x2
    ]
    [
        at 0x0 box system/view/screen-face/size effect [
            gradient 1x1 tan brown
        ] [unview]
        at 20x20 h1 blue "Slide 2"
        box black 2000x2
        text "Gradients and color effects are easy in REBOL:"
        box effect [gradient 123.23.56 254.0.12]
        box effect [gradient blue gold/2]
        text "Click anywhere on the screen to close..."
        box black 2000x2
    ]
]
foreach slide slides [
    view/options center-face layout slide 'no-title
]

A simple but fully functional retail cash register app:

REBOL [title: "Minimal Cash Register"]
view gui: layout [
    style fld field 80
    across
    text "Cashier:"   cashier: fld 
    text "Item:"      item: fld 
    text "Price:"     price: fld [
        if error? try [to-money price/text] [alert "Price error" return]
        append a/text reduce [mold item/text tab price/text newline]
        item/text: copy "" price/text: copy ""
        sum: 0
        foreach [item price] load a/text [sum: sum + to-money price]
        subtotal/text: form sum
        tax/text: form sum * .06
        total/text: form sum * 1.06 
        focus item
        show gui
    ]
    return
    a: area 600x300
    return
    text "Subtotal:"   subtotal: fld 
    text "Tax:"        tax: fld 
    text "Total:"      total: fld
    btn "Save" [
        items: replace/all (mold load a/text) newline " "
        write/append %sales.txt rejoin [
            items newline cashier/text newline now/date newline
        ]
        clear-fields gui
        a/text: copy ""             
        show gui             
    ]
]

A report program which calculates the daily sales total for the program above:

REBOL [title: "Daily Sales Total"]
sales: read/lines %sales.txt
sum: $0
foreach [items cashier date] sales [
    if now/date = to-date date [
        foreach [item price] load items [
            sum: sum + to-money price
        ]
    ]
]
alert rejoin ["Total sales today: " sum]

Below are two different version of the same group chat program. The first runs in the text console. The second uses a GUI window to allow the user to input text and view messages. The messages are just stored in a text file on a web server (to use this program, you need to know a valid web site FTP url, and use correct username and password info to write the file):

REBOL [title: "Group Chat (Console)"]
url: ftp://user:pass@site.com/public_html/chat
write/append url ""
name: copy ask "^LName:  "
forever [
    notes: copy read url
    message: ask rejoin [newpage notes "Message:  "]
    if message = "erase" [write url ""]
    if message <> "" [
        write/append url rejoin [
            now " (" name "):  " message "^/^/"
        ]
    ]
]

REBOL [title: "Group Chat (GUI)"]
url: ftp://user:pass@site.com/public_html/chat
write/append url ""
name: request-text/title "Name:"
view gui: layout [
    a: area 600x300
    text "New Message:"
    message: field 600 [
        if message/text = "erase" [write url ""]
        if message/text <> "" [
            write/append url rejoin [
                now " (" name "):  " message/text "^/^/"
            ]
        ]
        a/text: copy read url  focus message  show gui 
    ]
]

The Rebocalc app by Carl Sassenrath demonstrates how to create a tiny spreadsheet and this article explains and extends its features. This discussion at RebolForum.com produced the example below, which demonstrates an even simpler way to implement 'worksheet' features which run quickly even with hundreds of thousands of data values:

REBOL [title: "Work Sheet"]
sum: func [fld] [attempt [
    s: 0  foreach n to-block copy fld/text [s: s + (to-decimal n)]  s
]]
average: func [fld] [attempt [
    (sum fld) / (length? to-block copy fld/text)
]]
maxi: func [fld] [
    attempt [first maximum-of to-block copy fld/text
]]
compound: does [attempt [
    ((1 + (to-decimal rt/text)) ** to-integer yrs/text) *
    to-decimal p/text
]]
random/seed now
x: copy y: copy z: copy ""
loop 10000 [
     append x join random 10000 newline
     append y join random 10000 newline
     append z join random 10000 newline
]
view center-face layout [
     style col area 80x350
     style fld field 80
     style txt text 80
     across
     txt "Sum:"      txt "Average:" txt "Maximum:"     return
     a: col form x   b: col form y    c: col form z    return
     txt "Sum:"      txt "Average:" txt "Maximum:"     return
     fld [face/text: sum a      show face]
     fld [face/text: average b  show face]
     fld [face/text: maxi c     show face]             return
     txt                                               return
     txt txt "Principal:"  p:   fld "10000"            return
     txt txt "Years:"      yrs: fld "10"               return
     txt txt "Rate:"       rt:  fld ".07"              return
     txt txt "TOTAL:" fld [face/text: compound show face]
]

This version adds improved error handling and input validation, column sorting, data save and load capability, and other features:

REBOL [title: "Work Sheet"]
random/seed now
x: copy y: copy z: copy ""
loop 50000 [
    insert tail x join random 50000 newline
    insert tail y join random 50000 newline
    insert tail z join random 50000 newline
]
sum: func [fld] [
    s: 0  
    foreach num to-block copy fld/text [
        if error? try [
            s: s + (to-decimal num)
        ] [
            return join "Error: " num
        ]
    ] 
    s
]
average: func [fld] [
    attempt [
        the-sum: sum fld
        if attempt [find the-sum "error"] [return the-sum]
        the-sum / (length? to-block copy fld/text)
    ]
]
maxi: func [fld] [
    attempt [
        first maximum-of to-block copy fld/text
    ]
]
compound: func [f1 f2 f3] [
    attempt [
        (
            (1 + to-decimal f3/text) ** to-integer f2/text
        ) * to-decimal f1/text
    ]
]
ascend: false
srt: func [fld] [
    st: copy ""
    ascend: not ascend
    data: to-block copy fld/text
    sorted: copy either ascend [sort data] [sort/reverse data]
    foreach i sorted [insert tail st join i "^/"] 
    head st
]
view center-face gui: layout [
    style col area  100x250
    style fld field 100
    style txt text  100
    across
    txt "Sum:"      [set-face a srt a]
    txt "Average:"  [set-face b srt b]
    txt "Maximum:"  [set-face c srt c]                 return 
    a: col form x   b: col form y     c: col form z    return
    txt "SUM:"      txt "AVERAGE:"    txt "MAXIMUM:"   return 
    fld [face/text: sum a      show face]
    fld [face/text: average b  show face]   
    fld [face/text: maxi c     show face]              return
    txt                                                return
    txt txt "Principal:"  p:   fld "10000"             return
    txt txt "Years:"      yrs: fld "10"                return
    txt txt "Rate:"       rt:  fld ".07"               return
    txt txt "TOTAL:" fld [face/text: compound p yrs rt show face] return
    txt txt btn "Save" [
        write %worksheet.dat rejoin [
            mold a/text mold b/text mold c/text
            mold p/text mold yrs/text mold rt/text
        ]
    ]
    btn "Load" [
        if error? try [dat: load %worksheet.dat] [return]
        set-face a dat/1 set-face b dat/2 set-face c dat/3
        p/text: dat/4 yrs/text: dat/5 rt/text: dat/6
        show gui
    ]
]

To explore the potential capabilities of this app, you could, for example, add the ability to read data values from a Rebol CGI app running on a web server, make the results available to multiple users on a local network (via a TCP connection), send emails to a group of people whenever a set of calculations satisfies a given set of conditions, insert this app in a live full screen presentation, etc. Doing those things is simple with Rebol. Not so with spreadsheets.

9. Creating Web Applications using REBOL CGI

In order to create "CGI" applications for a web site, you need an available web server, and the REBOL interpreter must be uploaded and installed on your web server. If you don't have an online web server account, you can download a full featured free Apache web server package that will run on your local Windows PC, from http://www.uniformserver.com. This project contains the Uniform miniserver with Rebol all set up and ready to run CGI scripts.

9.1 An HTML Crash Course

In order to create any sort of web app, you need to understand a bit about HTML. HTML is the layout language used to format text and GUI elements on all web pages. HTML is not a programming language - it doesn't have facilities to process or manipulate data. It's simply a markup format that allows you to shape the visual appearance of text, images, and other items on pages viewed in a browser.

In HTML, items on a web page are enclosed between starting and ending "tags":

<STARTING TAG>Some item to be included on a web page</ENDING TAG>

There are tags to effect the layout in many ways. To bold some text, for example, surround it in opening and closing "strong" tags:

<STRONG>some bolded text</STRONG>

The code above appears on a web page as: some bolded text.

To italicize text, surround it in < i > and < / i > tags:

<i>some italicized text</i>

That appears on a web page as: some italicized text.

To create a table with three rows of data, do the following:

<TABLE border=1>
    <TR><TD>First Row</TD></TR>
    <TR><TD>Second Row</TD></TR>
    <TR><TD>Third Row</TD></TR>
</TABLE>

Notice that every

<opening tag>

in HTML code is followed by a corresponding

</closing tag>

Some tags surround all of the page, some tags surround portions of the page, and they're often nested inside one another to create more complex designs.

A minimal format to create a web page is shown below. Notice that the title is nested between "head" tags, and the entire document is nested within "HTML" tags. The page content seen by the user is surrounded by "body" tags:

<HTML>
    <HEAD>
        <TITLE>Page title</TITLE>
    </HEAD>
    <BODY>
        A bunch of text and <i>HTML formatting</i> goes here...
    </BODY>
</HTML>

If you save the above code to a text file called "yourpage.html", then click to open it, you'll see in your browser a page entitled "Page title", with the text "A bunch of text and HTML formatting goes here...". Upload that file to your web server, and surf to http://yoursite.com/yourpage.html , All web pages work that way - this tutorial is in fact just an HTML document stored on the author's web server account. Click View -> Source in your browser, and you'll see the HTML tags that were used to format this document (try doing that now).

9.1.1 HTML Forms and Server Scripts - the Basic CGI Model

The following HTML example contains a "form" tag inside the standard HTML head and body layout. Nested inside the form tags are a text input field tag, and a submit button tag:

<HTML>
    <HEAD><TITLE>Data Entry Form</TITLE></HEAD>
    <BODY>
        <FORM ACTION="http://yourwebserver.com/your_rebol_script.cgi">
            <INPUT TYPE="TEXT" NAME="username" SIZE="25">
            <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit">
        </FORM>
    </BODY>
</HTML>

Forms can contain tags for a variety of input types: multi-line text areas, drop down selection boxes, check boxes, etc. See http://www.w3schools.com/html/html_forms.asp for more information about form tags.

YOU CAN USE THE DATA ENTERED INTO ANY FORM BY POINTING THE action ADDRESS TO THE URL AT WHICH A SPECIFIC REBOL SCRIPT IS LOCATED. For example, 'FORM ACTION="http://yourwebserver.com/your_rebol_script.cgi"' in the above form could point to the URL of the following CGI script, which is saved as a text file on your web server. When a web site visitor clicks the submit button in the above form, the data is sent to the following program, which in turn does some processing, and prints output directly to the user's web browser. NOTE: Remember that in REBOL curly brackets are the SAME AS QUOTES. Curly brackets are used in all the following examples, because they allow for multiline content and they help improve readability by clearly showing where strings begin and end:

#!/home/your_user_path/rebol/rebol -cs
REBOL []
print {content-type: text/html^/}  
submitted: decode-cgi read-cgi

print {<HTML><HEAD><TITLE>Page title</TITLE></HEAD><BODY>}
print rejoin [{Hello } second submitted {!}]
print {</BODY></HTML>}

In order for the above code to actually run on your web server, a working REBOL interpreter must be installed in the path designated by "/home/your_user_path/rebol/rebol -cs". For example, if the REBOL interpreter is in the same path as the script, that line should read "#!./rebol -cs".

The first 4 lines of the above script are basically stock code. Include them at the top of every REBOL CGI script. Notice the decode-cgi read-cgi line - it's the key to retrieving data submitted by HTML forms. In the code above, the decoded data is assigned the variable name "submitted". The submitted form data can be manipulated however desired, and output is then returned to the user via the "print" function. That's important to understand: all data "print"ed by a REBOL CGI program appears directly in the user's web browser (i.e., to the web visitor who entered data into the HTML form). The printed data is typically laid out with HTML formatting, so that it appears as a nicely formed web page in the user's browser.

Any normal REBOL code can be included in a CGI script. You can perform any type of data storage, retrieval, organization, and manipulation that can occur in any other REBOL program. The CGI interface just allows your REBOL code to run online on your web server, and for data to be input/output via web pages which are also stored on the web server, accessible by any visitor's browser.

9.2 A Standard CGI Template to Memorize

Most short CGI programs typically print an initial HTML form to obtain data from the user. In the initial printed form, the action address typically points back to the same URL address as the script itself. The script examines the submitted data, and if it's empty (i.e., no data has been submitted), the program prints the initial HTML form. Otherwise, it manipulates the submitted data in way(s) you choose and then prints some output to the user's web browser in the form of a new HTML page. Here's a basic example of that process, using the code above:

#!/home/your_user_path/rebol/rebol -cs
REBOL []
print {content-type: text/html^/}
submitted: decode-cgi read-cgi

; The 4 lines above are the standard REBOL CGI headers.
; The line below prints the standard HTML, head and body
; tags to the visitor's browser:

print {<HTML><HEAD><TITLE>Page title</TITLE></HEAD><BODY>}

; Next, determine if any data has been submitted.
; Print the initial form if empty.  Otherwise, process 
; and print out some HTML using the submitted data.  
; Finally, print the standard closing "body" and "html"
; tags, which were opened above:

either empty? submitted [
    print {
        <FORM METHOD="POST">
        <INPUT TYPE="TEXT" NAME="username" SIZE="25">
        <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit">
        </FORM>
        </BODY></HTML>
    }
] [ 
    print rejoin [{Hello } submitted/2 {!}]
    print {</BODY></HTML>}
]

Using the above standard outline, you can include any required HTML form(s), along with all executable code and data required to make a complete CGI program, all in one script file. Memorize it.

10. Example CGI Applications

10.1 Generic CGI App, With HTML Form

Here is a basic CGI template that prints a form for user data entry. Learn a bit more about HTML forms, and process the submitted/x variables, and you can create some very powerful web apps. A demo of this script is available at http://guitarz.org/rebol3:

#!./rebol -cs
REBOL [title: "Generic CGI Application, With HTML Form"]
print {content-type: text/html^/}
print {<HTML><HEAD><TITLE>Page title</TITLE></HEAD><BODY>}
submitted: decode-cgi read-cgi
either empty? submitted [
    print {
        <FORM METHOD="POST">
        <INPUT TYPE="TEXT" NAME="username" SIZE="25">
        <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit">
        </FORM>
        </BODY></HTML>
    }
] [ 
    print rejoin [{Hello } submitted/2 {!}]
    print {</BODY></HTML>}
]

10.2 CGI Photo Album

Here's a simple CGI program that displays all photos in the current folder on a web site, using a foreach loop. A demo of this script is available at http://guitarz.org/rebol3/photos.cgi:

#!./rebol -cs
REBOL [title: "CGI Photo Album"]
print {content-type: text/html^/}
print {<HTML><HEAD><TITLE>Photos</TITLE></HEAD><BODY>}
folder: read %./
count: 0
foreach file folder [
    foreach ext [".jpg" ".gif" ".png" ".bmp"] [
        if find file ext [
            print [<BR> <CENTER>]
            print rejoin [{<img src="} file {">}]
            print [</CENTER>]
            count: count + 1
        ]
    ]
]
print join {<BR>Total Images: } count
print {</BODY></HTML>}

10.3 CGI Text Chat

Here's a short web chat app. Users can type the word "erase" in the name field to delete all former texts. A demo of this script is available at http://guitarz.org/rebol3/chat.cgi:

#!./rebol -cs
REBOL [title: "Group Chat"]
print {content-type: text/html^/}
url: %./chat.txt
write/append url ""
submitted: decode-cgi read-cgi
if submitted/2 = "erase" [write url ""]
if submitted/2 <> none [
    write/append url rejoin [
        now " (" submitted/2 "):  " submitted/4 "^/^/"
    ]
]
notes: copy read url
print rejoin [
    "<pre>" notes "</pre>"
    {<FORM METHOD="POST">
        Name:<br>
        <input type=text size="65" name="username"><br>
        Message:<br>
        <textarea name=message rows=5 cols=50></textarea><br>
        <input type="submit" name="submit" value="Submit">
    </FORM>}
]

10.4 A Generic Drop Down List Application

The following example demonstrates how to automatically build lists of days, months, times, and data read from a file, using dynamic loops (foreach, for, etc.). The items are selectable from drop down lists in the printed HTML form. A demo of this script is available at http://guitarz.org/rebol3/drop.cgi::

#!./rebol3 -cs
REBOL [title: "Dropdown Lists"]
print {content-type: text/html^/}
print {<HTML><HEAD><TITLE>Dropdown Lists</TITLE></HEAD><BODY>}
submitted: decode-cgi read-cgi
if not empty? submitted [
    print rejoin [{NAME SELECTED: } submitted/2 {<BR><BR>}]
    selected: rejoin [
        {TIME/DATE SELECTED: }
        submitted/4 { } submitted/6 {, } submitted/8
    ]
    print selected
    quit
]
; If no data has been submitted, print the initial form:
print {<FORM METHOD="POST">SELECT A NAME:  <BR> <BR>}
names: read/lines %users.txt
print {<select NAME="names">}
foreach name names [prin rejoin [{<option>} name]]
print {</option> </select> <br> <br>}
print { SELECT A DATE AND TIME: }
print rejoin [{(today's date is } now/date {)} <BR><BR>]
print {<select NAME="month">}
foreach m system/locale/months [prin rejoin [{<option>} m]]
print {</option> </select>}
print {<select NAME="date">} 
for daysinmonth 1 31 1 [prin rejoin [{<option>} daysinmonth]]
print {</option> </select>}
print {<select NAME="time">}
times: ["10am" "11am" "12pm" "1pm" "1:30pm" "4:15pm" "7:30pm"]
foreach time times [prin rejoin [{<option>} time]]
print {</option> </select><br> <br>}
print {<INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit"></FORM>}

The "users.txt" file used in the above example may look something like this:

nick
john
jim
bob

10.5 Summary

As with the CRUD, graphics, and other basic code examples you saw earlier, you'll notice that creating CGI apps is all about using variables, functions, series operations, loops, conditional evaluations and other similar language structures and approaches to managing data. In fact, ALL programming generally boils down to using those basic structures and ways of thinking. You'll find syntactic differences and variations in how such structures and principals are implemented in each language and development tool, but it always tends to reduce to thinking in essentially the same way about data, using all the basic concepts that were presented in the first section of this text. Whatever language(s) and/or tool(s) you use to create applications, those basic thought patterns about manipulating data with code, will always apply.

11. Learning More

Rebol is great for beginner programmers because you can learn to put all the basic language concepts to use very quickly and easily. But it's also an extremely powerful general purpose commercial development tool. You can use it to create complex software of all types for desktop, mobile, server, and web environments, with greater simplicity, and productivity incomparable to any other tool on the market. Because Rebol code is typically orders of magnitude shorter, more concise, and more readable than code written in popular tools such as Java, Visual Basic, and even scripting tools such as Python, Ruby, and Lua, developing with it means being able to create more software over your lifetime, and it makes maintaining, upgrading and improving that software much more manageable than it is with other development tools. Rebol's portability, small size, and unique features enable capabilities which are simply not possible using other development platforms. To learn more about Rebol, see http://business-programming.com for a full 850 book that documents everything you need to know to create any type of application with Rebol (over 100 complete working programs are included in that text) and http://learnrebol.com to learn about the newest version of Rebol (Saphir R3 and R3-GUI), which is open source and available for Android mobile platforms, as well as desktop and server environments.

Rebol is not a popular language, so if your goal is to obtain paid work writing code, you should plan on learning other tools. Java, C, C++, C#, Visual Basic, PHP, Python, Ruby, SQL, and HTML5 (HTML + CSS + Javascript) are the languages to learn. If you want to work as a professional developer, you'll not only need to learn the most popular languages, you'll also need to learn how to use all the most popular IDEs, libraries, and other associated tools in each language's environment (i.e., perhaps Eclipse, Netbeans, GWT, Tomcat, and a variety of specific Java APIs, if you want to work in a Java development environment). For any programming job, you'll be expected to know and use the tools which the project manager decides is appropriate for the project. If you want to do web site development work, you absolutely need to learn HTML, CSS, and Javascript, as well as PHP, SQL, and it wouldn't hurt to know Python and Ruby and their associated web frameworks (Django, Ruby on Rails, etc.). If you want to develop hardware drivers learn C, 3D games learn C++, server software learn Java. If you're not sure, start by studying Java or C#. They're used for lots of different types of development and there's lots of work available using only those languages. Compared to many other software development tools, there's also a more complete environment surrounding Java and C#, so the learning curve is eased by only having to study a single cohesive ecosystem. Even though the language in those ecosystems is often far more verbose than in Rebol, or scripting languages like Python, Ruby, Lua, etc., you don't need to root out and/or compare lots of 3rd party libraries, incompatible versions, etc., so the learning curve can be a bit more straightforward and consistent from beginning to end. Whatever programming language(s) you focus on, you'll likely need to learn SQL too, because database systems are used to store information in most types of programs.

No matter what tools you decide to learn and use, the fundamentals of programming will always apply. You need to start by understanding how things like variables, functions, lists, conditional evaluations, loops, objects, and other basic coding structures and processes fit together to create applications. Learning Rebol is useful because it's a uniquely pleasant and phenomenally productive tool to work with, for all sorts of serious work, whether you want to work professionally and produce commercial applications, or whether you just want to be able to produce custom apps that help you in your personal and private business life. No matter what other tools you end up mastering, Rebol can be a great tool to study while learning, because it's encouraging to discover such an immediate level of understanding, useful skill, and productive capability.