REBOL Programming For The Absolute Beginner

By: Nick Antonaccio

Updated: 3-24-2010

The new official URL for this tutorial is http://re-bol.com

An old version of this tutorial is available at http://musiclessonz.com/rebol_tutorial-old.html

Be sure to see the 68 YouTube video tutorials that cover this material (10 hours of video).

A simple introductory tutorial application for children is also available.

Contents:

1. Introducing REBOL

What is REBOL? Why use it? REBOL is a uniquely small and productive development tool that can be used to create powerful desktop software , dynamic CGI web site and server applications, rich distributed browser plugin applications, mobile apps, and more. REBOL's blend of capability, compact size, ease of use, cross-platform functionality, and variety of interpreter platforms enable it to gracefully replace many common tools such as Java, Python, Visual Basic, C, C++, PHP, Perl, Ruby, Javascript, toolkits such as wxWidgets, graphic/multimedia platforms such as Flash, DBMSs such as Access, MySQL, and SQLite, a variety of system utilities, and more, all with one simple paradigm. Despite its broad usefulness, REBOL is far easier to implement than any other comparable tool.

, dynamic CGI and server applications, rich distributed applications, mobile apps, and more. REBOL's blend of capability, compact size, ease of use, cross-platform functionality, and variety of interpreter platforms enable it to gracefully replace many common tools such as Java, Python, Visual Basic, C, C++, PHP, Perl, Ruby, Javascript, toolkits such as wxWidgets, graphic/multimedia platforms such as Flash, DBMSs such as Access, MySQL, and SQLite, a variety of system utilities, and more, all with one simple paradigm. Despite its broad usefulness, REBOL is far to implement than any other comparable tool. REBOL is ultra compact . Its uncompressed file size is about 1/2 Meg on most platforms. It can be downloaded, installed, and put to use on all supported operating systems in less than a minute , even over a slow dialup connection.

. Its uncompressed file size is about 1/2 Meg on most platforms. It can be downloaded, installed, and put to use on all supported operating systems in , even over a slow dialup connection. REBOL can also be used immediately, without installation, on over 40 operating systems as a lightweight file manager, text editor, calculator, database manager, email client, ftp client, news reader, image viewer/editor, OS shell, and more. You can use it as a simple utility program with a familiar interface to common computing activities, on just about any computer, even if you're unfamiliar with the operating system.

REBOL includes GUI, network, graphics, sound, database, image manipulation, math, parsing, compression, CGI decoding, secure network services, text editing, and other functions built-in . No external modules, tool kits, or IDEs are required for any essential functionality .

. . REBOL is easy enough for absolute beginners and average computer users to operate immediately, but powerful and rich enough for a wide variety of complex professional work.

enough for absolute beginners and average computer users to operate immediately, but enough for a wide variety of complex professional work. REBOL has useful built-in help for all available functions and language constructs.

for all available functions and language constructs. REBOL is supported by a friendly and knowledgeable community of active developers around the world.

REBOL is available in both free and supported commercial versions. The free version can be used to create commercial applications, with very few license restrictions. Part of the REBOL language is open source, and that code is available directly in the interpreter. The closed components are kept in an escrow account , in case the Rebol Technologies company ever goes out of business (source code escrow licenses are available for those who use it in critical work).

and supported commercial versions. The free version can be used to create commercial applications, with very few license restrictions. Part of the REBOL language is open source, and that code is available directly in the interpreter. The closed components , in case the Rebol Technologies company ever goes out of business (source code escrow licenses are available for those who use it in critical work). REBOL has a facility for ultra fast performance using "Rebcode", which can be optimized like assembly language, but works the same way across all supported hardware and operating systems (using the exact same code).

REBOL was created by Carl Sassenrath, who developed the Amiga operating system executive in 1985 (the first preemptive multitasking OS kernel for personal computers). REBOL has been in commercial use since its first release in 1998, and a 3rd major release of the language is in active development as of 2009.

REBOL is a modern, multi paradigm development tool (procedural, object oriented, and functional), but its unique syntax goes well beyond traditional approaches to computer language design. REBOL code is typically much shorter and more readable than other languages, and REBOL is often far more productive than other development tools (very often, dramatically so). There's absolutely no simpler solution for cross-platform GUI creation, anywhere (the code for a complete program window with a button is simply: view layout [button] ). But that just scratches the surface. REBOL has a striking ability to simplify difficult computing tasks of all types, with straightforward, high level code dialects . No other development tool is as adept at creating practical domain specific languages . On a more basic level, the storage/manipulation/transfer of all data is managed by a single ubiquitous code structure . Arrays, lists, tables, and even sizable databases of mixed text, code, and binary data are all stored using one consistent "block" syntax. Blocks are created simply by surrounding any list of data with square brackets. Like everything else in REBOL, the format is extremely simple, but it enables many powerful features for searching, sorting, comparing, dissecting, evaluating, storing, retrieving, transferring and otherwise manipulating information of all types. Common network protocols and data values are also natively usable in REBOL. You can read and write data directly to/from web servers, email accounts, databases, and more, add/subtract time, date, and other values automatically, manipulate XML, HTML, CSV and other formats natively, display and apply effects to images, play sounds, etc., all without any preparation, complex formatting, or use of any external library code. REBOL has a built in, powerful "parse" dialect which elegantly replaces the need for regular expressions in most cases. The list of such practical features is long, but REBOL is not built from simple gimmicks - it's a deep and powerful tool. Because so many practical computing elements are all built into REBOL, and interact natively , the learning curve required to get real work done is much easier than in other development environments. No other language includes such straightforward and versatile mechanisms for accomplishing the most basic work of computers - managing data of all types.

than other languages, and REBOL is often than other development tools (very often, so). (the code for a complete program window with a button is simply: ). But that just scratches the surface. REBOL has a striking ability to simplify difficult computing tasks of all types, with straightforward, high level . No other development tool is as adept at creating practical . On a more basic level, the storage/manipulation/transfer of all data is managed by a . Arrays, lists, tables, and even sizable databases of mixed text, code, and binary data are all stored using one consistent "block" syntax. Blocks are created simply by surrounding any list of data with square brackets. Like everything else in REBOL, the format is extremely simple, but it enables many powerful features for searching, sorting, comparing, dissecting, evaluating, storing, retrieving, transferring and otherwise manipulating information of all types. Common network protocols and data values are also natively usable in REBOL. You can read and write data directly to/from web servers, email accounts, databases, and more, add/subtract time, date, and other values automatically, manipulate XML, HTML, CSV and other formats natively, display and apply effects to images, play sounds, etc., all without any preparation, complex formatting, or use of any external library code. REBOL has a built in, powerful "parse" dialect which elegantly replaces the need for regular expressions in most cases. The list of such practical features is long, but REBOL is not built from simple gimmicks - it's a deep and powerful tool. Because so many practical computing elements are all built into REBOL, and interact , the learning curve required to is easier than in other development environments. No other language includes such straightforward and versatile mechanisms for accomplishing the most basic work of computers - managing data of all types. REBOL is small, practical, portable, extremely productive, and different than the typical mess of modern computing tools. It does not rely on a large stack of disparate technologies to accomplish useful computing goals. All it's features exist inside one tiny downloadable executable that anyone can get running, on just about any computer, in less than a minute. It can be used as anything from compact utility application to powerful professional development environment. Even average computer users with absolutely no coding experience can learn to create real, useful and powerful REBOL scripts very quickly. Here are a few screen shots of examples covered in this tutorial: Downloadable Windows executables of these programs are available at: http://musiclessonz.com/rebol_tutorial/examples

2. How This Tutorial Is Organized

There are 5 main parts to this text: Fundamentals:

The first sections cover how to use the REBOL interpreter (typing in the console, creating scripts, navigating built-in help, creating .exe's, etc.), basic language constructs and syntax (variables, functions, data types, conditions, loops, i/o, etc.), and the fundamentals of creating GUI program windows (~65 pages). Examples:

11 fully documented programs which demonstrate, line by line, how the above fundamentals are put together to form complete applications (~35 pages). Other Important Topics:

Graphics, animation, 3D, using databases, accessing DLLs and the OS API, web site CGI programming, writing multitasking code, 3rd party tool kits, parse, objects, ports, and more (~100 pages). Real World Case Studies:

21 full case studies covering how a wide variety of complete desktop, network, and web site applications were conceived and created using REBOL. This section demonstrates, step by step, how each of the examples grew from concept to final design using outlines, pseudo code, and detailed finished code. Each example demonstrates a variety of practical REBOL concepts, code patterns, and tools, and helps guide you towards "thinking in REBOL" (~230 pages). Additional Scripts and Resources:

More code and resources to help complete your understanding of REBOL and continue learning. This tutorial is less than 450 pages, yet it covers the REBOL language from the ground up, fully documents the creation of more than 50 applications, and contains many additional short scripts and useful concepts. Links to other online documentation resources are provided to more fully learn many topics, but no third party reference materials are required to understand any example in this text, even if you've never programmed a line of code before. If you're familiar with other programming languages, be prepared to think about coding in ways that are a bit different from your accustomed patterns. You won't find the typical explanations of object oriented programming, modules, pointers, arrays, string management, regular expressions, or other common topics in this text. That's because REBOL's design provides straightforward solutions to reduce or eliminate the need for many complex syntax structures and coding techniques you may know. Every step of the way through this tutorial, you'll pick up practical approaches to easily achieve computing goals of all types. By learning REBOL, you'll learn to get many things done more quickly and easily than you can with any other tool. Enjoy!

3. Getting Started: Downloading and Installing REBOL, Hello World

The REBOL interpreter is a program that runs on your computer. It translates written text in the REBOL language syntax ("source code") to instructions the computer understands. To get the free REBOL interpreter, go to: http://rebol.com/view-platforms.html For Microsoft Windows, download the rebview.exe file - just click the link with your mouse and save it to your hard drive. If you want to run REBOL on any other operating system (Macintosh, Linux, etc.), just select, download and run the correct file for your computer. It works the same way on every operating system. You can use the stand-alone versions on just about any desktop machine. Upload the correct interpreter version to your web server and you can also execute REBOL CGI programs directly on your web site. You can also install a plugin version to run full REBOL desktop applications directly on pages in a web browser. Once you've got the tiny REBOL desktop interpreter downloaded, installed, and running on your computer (Start -> Programs -> REBOL -> REBOL View), click the "Console" icon, and you're ready to start typing in REBOL programs. To run your first example, type the following line into the REBOL interpreter, and then press the [Enter] (return) key on your keyboard: alert "Hello world!" Before going any further, give it a try. Download REBOL and type in the code above to see how it works. It's extremely simple and literally takes just a few seconds to install. To benefit from this tutorial, type or paste each code example into the REBOL interpreter to see what happens. Install Notes: To enable REBOL/View console support in Ubuntu Linux, follow these instructions: Download the tar.gz file for Linux x86 - Fedora (Kernel 2.6). Open with archive manager (default). Look in the rebol-276 folder (or whatever version you've downloaded). Select and extract the "rebview" file into Ubuntu's HOME directory (the parent of the Ubuntu folder in the file system, or /home/ubuntu/ at the command line). In some versions, the file name is "rebol" instead of "rebview". Open a terminal window (Applications -> Accessories -> Terminal) Type "./rebview" (without the quotes), or "./rebol", depending on the version you've downloaded. On some versions of Linux, you may need to run "./rebview +i" to install the required libs.

4. An Amazingly Tiny Demo and Some Simple Examples

Many of the examples programs in this tutorial are available as downloadable Windows executables, at: http://musiclessonz.com/rebol_tutorial/examples To whet your appetite, here's an example that demonstrates just how potent REBOL code can be. The following script contains 10 useful programs in LESS THAN HALF A PRINTED PAGE OF CODE: FREEHAND PAINT: Draw and save graphic images SNAKE GAME: Eat the food, avoid hitting the walls and yourself TILE PUZZLE, "15": Arrange the tiles into alphabetical order CALENDAR: Save and view events for any date VIDEO: Live webcam video viewer (not just a static image) IPs: Display your LAN and WAN IP addresses EMAIL: Read emails from any pop account DAY CALCULATOR: Count the days between 2 selected dates PLAY SOUNDS: Browse your computer for wave files to play FTP TOOL: Web site editor (browse folders on your web server, click files to edit and save changes back to your server, create and edit new files, etc.) This example is 100% native REBOL code. No external libraries, images, GUI components, or other resources of any kind are imported or called. It runs on Windows, Mac, Linux, and any other OS supported by REBOL/View. To run it, just download the tiny REBOL interpreter and copy/paste the code below into the console (a Windows .exe is also available here): REBOL[title:"Demo"]p: :append kk: :pick r: :random y: :layout q: 'image z: :if gg: :to-image v: :length? g: :view k: :center-face ts: :to-string tu: :to-url sh: :show al: :alert rr: :request-date co: :copy g y[style h btn 150 h"Paint"[g/new k y[s: area black 650x350 feel[engage: func[f a e][ z a = 'over[p pk: s/effect/draw e/offset sh s]z a = 'up[p pk 'line]]] effect[draw[line]]b: btn"Save"[save/png %a.png gg s al"Saved 'a.png'"]btn "Clear"[s/effect/draw: co[line]sh s]]]h"Game"[u: :reduce x: does[al join{ SCORE: }[v b]unview]s: gg y/tight[btn red 10x10]o: gg y/tight[btn tan 10x10]d: 0x10 w: 0 r/seed now b: u[q o(((r 19x19)* 10)+ 50x50)q s(((r 19x19)* 10)+ 50x50)]g/new k y/tight[c: area 305x305 effect[draw b]rate 15 feel[engage: func[f a e][z a = 'key[d: select u['up 0x-10 'down 0x10 'left -10x0 'right 10x0]e/key]z a = 'time[z any[b/6/1 < 0 b/6/2 < 0 b/6/1 > 290 b/6/2 > 290][x]z find(at b 7)b/6[x]z within? b/6 b/3 10x10[p b u[q s(last b)]w: 1 b/3:((r 29x29)* 10)]n: co/part b 5 p n(b/6 + d)for i 7(v b)1[ either(type?(kk b i)= pair!)[p n kk b(i - 3)][p n kk b i]]z w = 1[clear( back tail n)p n(last b)w: 0]b: co n sh c]]]do[focus c]]]h"Puzzle"[al{ Arrange tiles alphabetically:}g/new k y[origin 0x0 space 0x0 across style p button 60x60[z not find[0x60 60x0 0x-60 -60x0]face/offset - x/offset[ exit]tp: face/offset face/offset: x/offset x/offset: tp]p"O"p"N"p"M"p"L" return p"K"p"J"p"I"p"H"return p"G"p"F"p"E"p"D"return p"C"p"B"p"A"x: p white edge[size: 0]]]h"Calendar"[do bx:[z not(exists? %s)[write %s ""]rq: rr g/new k y[h5 ts rq aa: area ts select to-block(find/last(to-block read %s)rq)rq btn"Save"[write/append %s rejoin[rq" {"aa/text"} "]unview do bx]] ]]h"Video"[wl: tu request-text/title/default"URL:"join"http://tinyurl.com" "/m54ltm"g/new k y[image load wl 640x480 rate 0 feel[engage: func[f a e][ z a = 'time[f/image: load wl show f]]]]]h"IPs"[parse read tu join"http://" "guitarz.org/ip.cgi"[thru<title>copy my to</title>]i: last parse my none al ts rejoin["WAN: "i" -- LAN: "read join dns:// read dns://]]h"Email"[ g/new k y[mp: field"pop://user:pass@site.com"btn"Read"[ma: co[]foreach i read tu mp/text[p ma join i"^/^/^/^/^/^/"editor ma]]]]h"Days"[g/new k y[ btn"Start"[sd: rr]btn"End"[ed: rr db/text: ts(ed - sd)show db]text{Days Between:}db: field]]h"Sounds"[ps: func[sl][wait 0 rg: load sl wf: 1 sp: open sound:// insert sp rg wait sp close sp wf: 0]wf: 0 change-dir %/c/Windows/media do wl:[wv: co[]foreach i read %.[z %.wav = suffix? i[p wv i]]]g/new k y[ft: text-list data wv[z wf <> 1[z error? try[ps value][al "Error"close sp wf: 0]]]btn"Dir"[change-dir request-dir do wl ft/data: wv sh ft]]]h{FTP}[g/new k y[px: field"ftp://user:pass@site.com/folder/"[ either dir? tu va: value[f/data: sort read tu va sh f][editor tu va]]f: text-list[editor tu join px/text value]btn"?"[al{Type a URL path to browse (nonexistent files are created). Click files to edit.}]]]] That's the entire application - all 10 programs. Go ahead, give it a try. Download the REBOL interpreter and copy/paste the code above into the console. It only takes a few seconds. By the end of this tutorial you'll know exactly how all that code works, and much more... 4 - Several Basic Examples The above example is obfuscated to demonstrate just how malleable and compact REBOL code can be. The following examples represent more typical, readable REBOL code. This first example demonstrates how to create a basic GUI program window (the size info in this example is optional): view layout [size 500x400] Here's a program window with a text area and a button: view layout [area btn "Click Me"] In this example, the button does something when clicked: view layout [ area btn "Click Me" [alert "You can type in the square area."] ] The following example demonstrates how the button can be made to save any text typed into the area, to a file on the hard drive: view layout [ a: area btn "Save" [ write %reboltut.txt a/text alert "Saved" ] ] Here's a little text editor application that builds on the idea above. You can likely get a sense of how it works just by glancing through the code: view layout [ h1 "Text Editor:" f: field 600 "filename.txt" a: area 600x350 across btn "Load" [ f/text: request-file show f a/text: read to-file f/text show a ] btn "Save" [ write to-file request-file/save/file f/text a/text alert "Saved" ] ] Here's an email client you can use to read and send emails to/from any pop/smtp server: view layout[ h1 "Send:" btn "Server settings" [ system/schemes/default/host: request-text/title "SMTP Server:" system/schemes/pop/host: request-text/title "POP Server:" system/schemes/default/user: request-text/title "SMTP User Name:" system/schemes/default/pass: request-text/title "SMTP Password:" system/user/email: to-email request-text/title "Your Email Addr:" ] a: field "user@website.com" s: field "Subject" b: area btn "Send"[ send/subject to-email a/text b/text s/text alert "Sent" ] h1 "Read:" f: field "pop://user:pass@site.com" btn "Read" [editor read to-url f/text] ] As you can see, REBOL is typically very easy to read and write.

4.1 Opening REBOL Directly to the Console

Before typing in or pasting any more code, adjust the following option in the REBOL interpreter: click the "User" menu in the graphic Viewtop that opens by default with REBOL, and uncheck "Open Desktop On Startup". That'll save you the trouble of clicking the "Console" button every time you start REBOL.

5. Some Perspective for Absolute Beginners

This tutorial moves at a pace quick enough to satisfy experienced developers, but because REBOL's learning curve is different from other programming languages, it can also be understood clearly by beginners. If you're reading this text as a novice programmer, it can be helpful to understand a few basic concepts that provide perspective about learning to program. First: Essentially, all computers do is let users input, store, retrieve, organize, share/transfer, manipulate, alter, view and otherwise deal with data in useful ways. So, everything you'll do when writing code basically involves manipulating text, numbers, and/or binary data (photos, music, etc.). The fundamental components used to deal with data haven't changed too dramatically in the past few decades. They've simply improved in speed, capacity, and interface. In the current state of modern computing, data is typically input, manipulated, and returned via graphical user interfaces such as program windows, web forms displayed in browsers, and other keyboard/mouse driven "GUI"s. Data is saved on local hard drives and storage devices (CDs, thumb drives, etc.) and on remote web servers, and is typically transferred via local networks and Internet connections. Images, sounds, video, and other types of multimedia data are contained in standardized file formats, and graphic data is displayed using standard mathematical techniques. Knowing how to control those familiar computing elements to allow users to manipulate data, is the goal of learning to program. It doesn't matter whether you're interested in writing business applications to work with inventory and scheduling (text and number data), programs to alter web pages (text and image data), programs to play/edit music (binary data), programs to broadcast video across the Internet (rapidly transferred sequential frames of binary data), programs to control robotic equipment, compute scientific equations, play games, etc... They all require learning to input, manipulate, and return data of some sort. You can do all those things with REBOL, and once you've done it in one language, it's easier to do with other programming tools. REBOL allows programmers to quickly build graphic interfaces to input and return all common types of data. It can easily manipulate text, graphics, and sounds in useful ways, and it provides simple methods to save, retrieve, and share data across all types of hardware, networks, and the Internet. That makes it a great way to begin learning how to program. By learning REBOL, you'll learn about all the fundamental structures and concepts in programming: variables, functions, data types, conditional operations, loops, objects, etc. You'll also learn about important topics such as user interface design, algorithmic thinking, working with databases, the operating system API, CGI, and more. Those topics all share conceptual and technical similarities, regardless of language, and you'll need to learn to think in those terms to write computer programs, even in a language that's as easy to learn as REBOL. Despite its ease of use, REBOL is an extremely powerful tool. For years it has been used by professionals in enterprise level work around the world. You may never need to learn another programming language. If you've never done any real "programming" before, the first part of this text may seem a bit technical. Don't be put off. There is no other language with a faster learning curve than REBOL - you'll begin to see the big picture within a few days. Working through this tutorial, you'll gradually build recognition of REBOL language idioms and practical code patterns, by example. The first part of the tutorial will be a whirlwind introduction to many of the fundamental language elements. Just take it all in, and if you really want to learn, be sure to type in, or at least copy/paste, each example into the REBOL interpreter. Reading through the code isn't enough.

6. A Quick Summary of the REBOL Language

6.1 Built-In Functions and Basic Syntax

As with any modern programming language, to use REBOL, you need to learn how to use "functions". Functions are words that perform actions. Function words are followed by data "parameters" (also called "arguments"). Paste these functions into the REBOL interpreter to see how they work: alert "Alert is a function. THIS TEXT IS ITS PARAMETER." request "Are you having fun yet?" editor "Edit this text." browse http://rebol.com Some functions don't require any data parameters, but do produce "return" values. Try these functions in the interpreter. They each return a value selected by the user: request-pass request-date request-color request-file The return values output by the above functions can be used in your programs to accomplish useful goals. The file name output by the "request-file" function, for example, could be used to determine which data gets opened and manipulated in your program. The data returned by the "request-pass" function can be used to control access to selected data. Many functions have optional or limited parameters/return values. These options, called "refinements", are specified by the "/" symbol. Try these variations of the "request-pass" function to see how they each perform differently: request-pass/only request-pass/user "username" request-pass/title "The 'title' refinement sets this header text." request-pass/offset/title 10x100 "'offset' repositions the requester." Some functions take multiple arguments. The "rejoin" function returns the joined ("concatenated") text arguments inside brackets. Concatenation is very important in all types of programming - you will see this function in use often: rejoin ["Hello " "there" "!"] 6.1.1 Understanding Return Values and the Order of Evaluation In REBOL, you can put as many functions as you want on one line, and they are all evaluated strictly from left to right. Functions are grouped together automatically with their required data parameter(s). The following line contains two alert functions: alert "First function" alert "Second function" Rebol knows to look for one parameter after the first alert function, so it uses the next piece of data on that line as the argument for that function. Next on the line, the interpreter comes across another alert function, and uses the following text as it's data parameter. In the following line, the first function "request-pass/offset/title" requires two parameters, so REBOL uses the next two items on the line ("10x100" and "title") as its arguments. After that's complete, the interpreter comes across another "alert" function, and uses the following text, "Processing", as its argument: request-pass/offset/title 10x100 "title" alert "Processing" IMPORTANT: In REBOL, the return values (output) from one function can be used directly as the arguments (input) for other functions. Everything is simply evaluated from left to right. In the line below, the "alert" function takes the next thing on the line as it's input parameter, which in this case is not a piece of data, but a function which returns some data (the concatenated text returned by the "rejoin" function): alert rejoin ["Hello " "there" "!"] To say it another way, the value returned above by the "rejoin" function is passed to (used as a parameter by) the "alert" function. Parentheses can be used to clarify which expressions are evaluated and passed as parameters to other functions. The parenthesized line below is treated by the REBOL interpreter exactly the same as the line above - it just lets you see more clearly what data the "alert" function puts on screen: alert ( rejoin ["Hello " "there" "!"] ) Perhaps the hardest part of getting started with REBOL is understanding the order in which functions are evaluated. The process can appear to work backwords at times. In the example below, the "editor" function takes the next thing on the line as it's input parameter, and edits that text. In order for the editor function to begin its editing operation, however, it needs a text value to be returned from the "request-text" function. The first thing the user sees when this line runs, therefore, is the text requester. That appears backwards, compared to the way it's written: editor (request-text) Always remember that lines of REBOL code are evaluated from left to right. If you use the return value of one function as the argument for another function, the execution of the whole line will be held up until the necessary return value is processed. Any number of functions can be written on a single line, with return values cascaded from one function to the next: alert ( rejoin ( ["You chose: " ( request "Choose one:" ) ] ) ) The line above is typical of common REBOL language syntax. There are three functions: "alert", "rejoin", and "request". In order for the first alert function to complete, it needs a return value from "rejoin", which in turn needs a return value from the "request" function. The first thing the user sees, therefore, is the request function. After the user responds to the request, the selected response is rejoined with the text "You chose: ", and the joined text is displayed as an alert message. Think of it as reading "display (the following text joined together ("you chose" (an answer selected by the user))). To complete the line, the user must first answer the question. To learn REBOL, it's essential to first memorize and recognize REBOL's many built-in function words, along with the parameters they accept as input, and the values which they return as output. When you get used to reading lines of code as functions, arguments, and return values, read from left to right, the language will quickly begin to make sense. It should be noted that in REBOL, math expressions are evaluated from left to right like all other functions. There is no "order of precedence", as in other languages (i.e., multiplication doesn't automatically get computed before addition). To force a specific order of evaluation, enclose the functions in parentheses: print (10 + 12) / 2 ; 22 / 2 = 11 (same as without parentheses) print 10 + (12 / 2) ; 10 + 6 = 16 REBOL's left to right evaluation is simple and consistent. Parentheses can be used to clarify the flow of code, if ever there's confusion. 6.1.2 White Space Unlike other languages, REBOL does not require any line terminators between expressions (functions, parameters, etc.), and you can insert empty white space (tabs, spaces, newlines, etc.) as desired into code. Text after a semicolon and before a new line is treated as a comment (ignored entirely by the interpreter). The code below works exactly the same as the previous example. Notice that tabs are used to indent the block of code inside the square brackets and that the contents of the brackets are spread across multiple lines. This helps group the code together visually, but it's not required: alert rejoin [ "You chose: " ; 1st piece of joined data (request "Choose one:") ; 2nd piece of joined data ] ONE CAVEAT: parameters for most functions should begin on the same line as the function word. The following example will not work properly because the rejoin arguments' opening brackets need to be on the same line as the rejoin function: alert rejoin ; This does NOT work. [ ; Put this bracket on the line above. "You chose: " (request "Choose one:") ] If you want to comment out a large section of code, simply surround it with curly braces: { This line doesn't do anything. This line also does nothing. This line is ignored too. }

6.2 More Basics: Word Assignment, I/O, Files, Built-In Data Types and Native Protocols

In REBOL, the colon (":") symbol is used to assign word labels ("variables") to values: person: "John" Now, the word label "person" can be used anywhere (without the colon), to represent the text "John". Notice that the variable "person" has been rejoined with some other text below: alert rejoin ["The person's name is " person] Word labels are NOT case sensitive: alert person alert PERSON alert PeRsOn ; to the REBOL interpreter, all three lines above are the same In this next example, the word "filename" is assigned to the value returned by the request-file function (a file chosen by the user): filename: request-file Now, the label "filename" can be used to represent the file selected above: alert rejoin ["You chose " filename] REBOL is a bit different from other programming languages in that word labels can be assigned to anything: numbers, text strings, binary data, arrays, lists, hash tables, functions, and even executable blocks of code. At this point, just be aware that when you see the colon symbol, a word label is being assigned to some value. The "ask" function is a simple way to get some text data from a user at the interpreter's command line (similar to "request-text", but without using a pop-up requester): ask "What is your name? " In the example below, the variable "name" is assigned to the text returned by the ask function (i.e., entered by the user). Again, the parentheses are not required - they're just there to clarify the grouping together of the 'ask' function with its text argument: name: (ask "What is your name? ") Now you can use the variable word "name" to represent whatever text the user typed in response to the above question. The "print" function is a simple way to display text data at the interpreter's command line: print rejoin ["Good to meet you " name] The "prin" function prints consecutive text elements right next to each other (not on consecutive lines): prin "All " prin "on " prin "one " print "line." print "On another." Multi-line formatted text is enclosed in curly braces ("{}"), instead of quotes: print { Line 1 Line 2 Line 3 } Quotes and curly braces can be used interchangeably on a single line: print {"text"} print "{text}" You can print a carriage return using the word "newline" or the characters ^/ print rejoin ["This text if followed by a carriage return." newline] print "This text if followed by a carriage return.^/" Clear the screen using "newpage": prin newpage The "write" function saves data to a file. It takes two parameters: a file name to write to, and some data to write to that file. write %/C/YOURNAME.txt name NOTE: in REBOL, the percent character ("%") is used to represent local files. Because REBOL can be used on many operating systems, and because those operating systems all use different syntax to refer to drives, paths, etc., REBOL uses the universal format: %/drive/path/path/.../file.ext . For example, "%/c/windows/notepad.exe" refers to "C:\Windows\Notepad.exe" in Windows. REBOL converts that syntax to the appropriate operating system format, so that your code can be written once and used on every operating system, without alteration. The following 2 functions convert REBOL file format to your operating system's format, and visa versa: to-local-file %/C/YOURNAME.txt to-rebol-file "C:\YOURNAME.txt" You can write data to a web site (or any other connected protocol) using the exact same write syntax that is used to write to a file (be sure to use an appropriate username and password for your web site ftp account): write ftp://user:pass@website.com/name.txt name The "read" function reads data from a file: print (read %/C/YOURNAME.txt) REBOL has a built-in text editor that can also read, write, and manipulate text data: editor %/c/YOURNAME.txt You can read data straight from a web server, an ftp account, an email account, etc. using the same format. Many Internet protocols are built right into the REBOL interpreter. They're understood natively, and REBOL knows exactly how to connect to them without any preparation by the programmer: editor http://rebol.com ; Reads the content of the ; document at this URL. editor pop://user:pass@website.com ; Reads all emails in this ; POP inbox. editor clipboard:// ; Reads data that has ; been copied/pasted to ; the OS clipboard. print read dns://msn.com ; Displays the DNS info ; for this address. print read nntp://public.teranews.com ; (Hit the [ESC] key to stop ; this Usenet listing.) ; NOTE: The editor reads, AND allows you to SAVE EDITS back to the server: editor ftp://user:pass@website.com/public_html/index.html Transferring data between devices connected by any supported protocol is easy - just read and write: ; read data from a web site, and paste it into the local clipboard: write clipboard:// (read http://rebol.com) ; afterward, try pasting into ; your favorite text editor ; read a page from one web site, and write it to another: write ftp://user:pass@website2.com (read http://website1.com) ; again, notice that the "write" function takes TWO parameters Sending email is just as easy, using a similar syntax: send user@website.com "Hello" send user@website.com (read %file.txt) ; sends an email, with ; file.txt as the body The "/binary" modifier is used to read or write binary (non-text) data. You'll use read/binary and write/binary to read and write images, sounds, videos and other non-text files: write/binary %/c/bay.jpg read/binary http://rebol.com/view/bay.jpg For clarification, remember that the write function takes two parameters. The first parameter above is "%/c/bay.jpg". The second parameter is the binary data read from http://rebol.com/view/bay.jpg: write/binary (%/c/bay.jpg) (read/binary http://rebol.com/view/bay.jpg) The "load" and "save" functions also read and write data, but in the process, automatically format certain data types for use in REBOL. Try this: ; assign the word "picture" to the image "load"ed from a given URL: picture: load http://rebol.com/view/bay.jpg ; save the image to a given file name, and automatically convert it ; to .png format; save/png %/c/picture.png picture ; show it in a GUI window (much more about this in the next section): view layout [image load %/c/picture.png] "Load" and "save" are used to conveniently manage certain types of data in formats directly usable by REBOL (images, sounds, DLLs, certain native data structures, etc. can be loaded and used immediately). You'll use "read" and "write" more commonly to store and retrieve typical types of data, exactly byte for byte, to/from a storage medium, when no conversion or formatting is necessary. REBOL automatically knows how to perform appropriate computations on times, dates, IP addresses, coordinate values, and other common types of data: print 3:30am + 00:07:19 ; increment time values properly print now ; print current date and time print now + 0:0:30 ; print 30 seconds from now print now - 10 ; print 10 days ago print 23x54 + 19x31 ; easily add coordinate pairs print 192.168.1.1 + 000.000.000.37 ; easily increment ip addresses view layout [image picture effect [flip]] ; apply effects to image types REBOL also natively understands how to use URLs, email addresses, files/directories, money values, tuples, hash tables, sounds, and other common values in expected ways, simply by the way the data is formatted. You don't need to declare, define, or otherwise prepare such types of data as in other languages - just use them. To determine the type of any value, use the "type?" function: some-text: "This is a string of text" ; strings of text go between type? some-text ; "quotes" or {curly braces} an-integer: 3874904 ; integer values are just pos- type? an-integer ; itive/negative whole numbers a-decimal: 7348.39 ; decimal numbers are recognized type? a-decimal ; by the decimal point web-site: http://musiclessonz.com ; URLs are recognized by the type? web-site ; http://, ftp://, etc. email-address: user@website.com ; email values are in the type? email-address ; format user@somewebsite.domain the-file: %/c/myfile.txt ; files are preceded by the % type? the-file ; character bill-amount: $343.56 ; money is preceded by the $ type? bill-amount ; symbol html-tag: <br> ; tags are places between <> type? html-tag ; characters binary-info: #{ddeedd} ; binary data is put between type? binary-info ; curly braces and preceded by ; the pound symbol image: load http://rebol.com/view/bay.jpg ; REBOL can even automatically type? image ; recognize the data type of ; most common image formats. a-sound: load %/c/windows/media/tada.wav ; And sounds too! a-sound/type Data types can be specifically "cast" (created, or assigned to different types) using "to-(type)" functions: numbr: 4729 ; The label 'numbr now represents the integer ; 4729. strng: to-string numbr ; The label 'strng now represents a piece of ; quoted text made up of the characters ; "4729". Try adding strng + numbr, and ; you'll get an error. ; This example creates and adds two coordinate pairs. The pairs are ; created from individual integer values, using the "to-pair" function: x: 12 y: 33 q: 18 p: 7 pair1: to-pair rejoin [x "x" y] ; 12x33 pair2: to-pair rejoin [q "x" p] ; 18x7 print pair1 + pair2 ; 12x33 + 18x7 = 30x40 ; This example builds and manipulates a time value using the "to-time" ; function: hour: 3 minute: 45 second: 00 the-time: to-time rejoin [hour ":" minute ":" second] ; 3:45am later-time: the-time + 3:00:15 print rejoin ["3 hours and 15 seconds after 3:45 is " later-time] ; This converts REBOL color values (tuples) to HTML colors and visa versa: to-binary request-color to-tuple #{00CD00} REBOL has many built-in helper functions for dealing with common data types. Another way to create pair values is with the "as-pair" function. You'll see this sort of pair creation commonly in games which plot graphics at coordinate points on the screen: x: 12 y: 33 q: 18 p: 7 print (as-pair x y) + (as-pair q p) ; much simpler! Built-in network protocols, native data types, and consistent language syntax for reading, writing, and manipulating data allow you to perform common coding chores easily and intuitively in REBOL. Remember to type or paste every example into the REBOL interpreter to see how each function and language construct operates.

6.3 GUIs (Program Windows)

Graphic user interfaces ("GUI"s) are easier to create in REBOL than in any other language. The functions "view" and "layout" are used together to display GUIs. The parameters passed to the layout function are enclosed in brackets. Those brackets can include identifiers for all types of GUI elements ("widgets"): view layout [btn] ; creates a GUI with a button view layout [field] ; creates a GUI with a text input field view layout [text "REBOL is really pretty easy to program"] view layout [text-list] ; a selection list view layout [ button field text "REBOL is really pretty easy to program." text-list check ] In REBOL, widgets are called "styles", and the entire GUI dialect is called "VID". You can adjust the visual characteristics of any style in VID by following it with appropriate modifiers: view layout [ button red "Click Me" field "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 ] The size of your program window can be specified by either of these two formats: view layout [size 400x300] view layout/size [] 400x300 ; both these lines do exactly the same thing A variety of functions are available to control the alignment, spacing, and size of elements in a GUI layout: view layout [ size 500x350 across btn "side" btn "by" btn "side" return btn "on the next line" tab btn "over a bit" tab btn "over more" below btn 160 "underneath" btn 160 "one" btn 160 "another" at 359x256 btn "at 359x256" ] VERY IMPORTANT: You can have widgets perform functions when clicked, or when otherwise activated. Just put the functions inside another set of brackets after the widget. This is how you get your GUIs to 'do something' (using the fundamentals introduced in the previous section): view layout [button "click me" [alert "You clicked the button."]] view layout [btn "Display Rebol.com HTML" [editor read http://rebol.com]] view layout [btn "Write current time to HD" [write %time.txt now/time]] ; 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."] button "Quit" [quit] ] To react to right-button mouse clicks on a widget, put the functions to be performed inside a second set of brackets after the widget: view layout [ btn "Right Click Me" [alert "left click"][alert "right click"] ] You can assign keyboard shortcuts (keystrokes) to any widget, so that pressing the key reacts the same way as activating the GUI widget: view layout [ btn "Click me or press the 'A' key on your keyboard" #"a" [ alert "You just clicked the button OR pressed the 'A' key" ] ] You can assign a word label to any widget, and refer to data and properties of that widget by its label. The "text" property is especially useful: view layout [ page-to-read: field "http://rebol.com" ; page-to-read/text now refers to the text contained in that field btn "Display HTML" [editor read (to-url page-to-read/text)] ] You can also set various properties of a widget using its assigned label. When the "Edit HTML Page" button is clicked below, the text of the multi-line area widget is set to contain the text read from the given URL. The "show" function in the example below is very important. It must be used to update the GUI display any time a widget property is changed (if you ever create a GUI that doesn't seem to respond properly, the first thing to check is that you've used a "show" function to properly update any changes on screen): view layout [ page-to-read: field "http://rebol.com" the-html: area 600x440 btn "Download HTML Page" [ the-html/text: read (to-url page-to-read/text) ; try commenting out the following line to see what happens: show the-html ] ] Below are two more examples of the above code pattern (getting and setting a widget's text property) - it's a very important idiom in REBOL GUIs. In the first example, the variable "f" is assigned to the field widget, then the variable "t" is assigned to the text contained in that field. In the second example, "t" is assigned the text contained in the f1 field, then the text in f2 is set to "t" - again, all using the colon symbol. Note the use of the "show" function to update the display: view layout [ f: field btn "Display Variable" [ ; When the button is pressed, set the variable ; "t" to hold the text currently in the field ; above, then alert the contents of that variable: t: f/text alert t ] ] view layout [ f1: field btn "Display Variable" [ ; Set the variable "t" to the text contained ; in the f1 field above: t: f1/text ; Now CHANGE the text in the f2 below to ; to equal the text stored in variable "t": f2/text: t show f2 ] f2: field ] ; You GET the text from a widget by assigning a VALUE to equal the ; widget's text property. You SET/CHANGE the text of a widget by ; assigning THE TEXT PROPERTY of that widget to equal a value. The "offset" of a widget holds its coordinate position. It's another useful property, especially for GUIs which involve movement: view layout [ size 600x440 jumper: button "click me" [ jumper/offset: random 580x420 ; The "random" function creates a random value within ; a specified range. In this example, it creates a ; random coordinate pair within the range 580x420, ; every time the button is clicked. That random value ; is assigned to the position of the button. ] ] The "style" function is very powerful. It allows you to assign a specific widget definition, including all its properties and actions, to any word label you choose. Any instance of that word label is thereafter treated as a replication of the entire widget definition: view layout [ size 600x440 style my-btn btn green "click me" [ face/offset: random 580x420 ] ; "my-btn" now refers to all the above code at 254x84 my-btn at 19x273 my-btn at 85x348 my-btn at 498x12 my-btn at 341x385 my-btn ] REBOL is great at dealing with all types of common data - not just text. You can easily display photos and other graphics in your GUIs, play sounds, display web pages, etc. Here's some code that downloads an image from a web server and displays it in a GUI - notice the "view layout" functions again: view layout [image (load http://rebol.com/view/bay.jpg)] The "image" widget inside the brackets displays a picture (.bmp, .jpg, .gif., .png) in the GUI. The "load" function downloads the image to be displayed. REBOL can apply many built-in effects to images: view layout [image (load http://rebol.com/view/bay.jpg) effect [Emboss]] view layout [image (load http://rebol.com/view/bay.jpg) effect [Flip 1x1]] ; The parentheses are not required: view layout [image load http://rebol.com/view/bay.jpg effect [Grayscale]] ; There are MANY more built-in effects. You can impose images onto most types of widgets: view layout [area load http://rebol.com/view/bay.jpg] ; Use the "fit" effect to stretch or shrink the size of the image to that ; of the widget: view layout [area load http://rebol.com/view/bay.jpg effect [Fit]] view layout [button load http://rebol.com/view/bay.jpg effect [Fit]] view layout [field load http://rebol.com/view/bay.jpg effect [Fit Emboss]] ; You can still type into the field and area widgets, as usual. You can apply colors directly to images, just like any other widget. Notice that you can perform calculations directly on color values: view layout [image load http://rebol.com/view/bay.jpg yellow] view layout [image load http://rebol.com/view/bay.jpg (yellow / 2)] view layout [image load http://rebol.com/view/bay.jpg (yellow + 0.0.132)] Color gradients (fades from one color to another) are also simple to apply to any widget: view layout [area effect [gradient red blue]] view layout [ size 500x400 backdrop effect [gradient 1x1 tan brown] box effect [gradient 123.23.56 254.0.12] box effect [gradient blue gold/2] ] You can assign a word label to any layout of GUI widgets, and then display those widgets simply by using the assigned word: gui-layout1: [button field text-list] view layout gui-layout1 You can save any GUI layout as an image, using the "to-image" function. This enables a built in screen shot mechanism, and also allows you to easily create/save/manipulate new images using any of the graphic capabilities in REBOL: ; assign the label "picture" to an image of a layout: picture: to-image layout [ page-to-read: field "http://rebol.com" btn "Display HTML" ] ; save it to the hard drive as a .png file: save/png %/c/layout.png picture Here are some other GUI elements used in REBOL's "VID" layout language: view layout [ backcolor white h1 "More GUI Examples:" box red 500x2 bar: progress slider 200x16 [bar/data: value show bar] area "Type here" drop-down across toggle "Click" "Here" [print value] rotary "Click" "Again" "And Again" [print value] choice "Choose" "Item 1" "Item 2" "Item 3" [print value] radio radio radio led arrow return text "Normal" text "Bold" bold text "Italic" italic text "Underline" underline text "Bold italic underline" bold italic underline text "Serif style text" font-name font-serif text "Spaced text" font [space: 5x0] return h1 "Heading 1" h2 "Heading 2" h3 "Heading 3" h4 "Heading 4" tt "Typewriter text" code "Code text" below text "Big" font-size 32 title "Centered title" 200 across vtext "Normal" vtext "Bold" bold vtext "Italic" italic vtext "Underline" underline vtext "Bold italic underline" bold italic underline vtext "Serif style text" font-name font-serif vtext "Spaced text" font [space: 5x0] return vh1 "Video Heading 1" vh2 "Video Heading 2" vh3 "Video Heading 3" vh4 "Video Heading 3" label "Label" below vtext "Big" font-size 32 banner "Banner" 200 ] Here's a list of all the built in widgets (remember, in REBOL's VID language, widgets are called "styles"): probe extract svv/vid-styles 2 Here's a list of the changeable attributes ("facets") available to all widgets: probe remove-each i copy svv/facet-words [function? :i] And here's a list of available layout words: probe svv/vid-words That's just the tip of the iceberg. With REBOL, even absolute beginners can create nice looking, powerful graphic interfaces in minutes. See http://rebol.com/docs/easy-vid.html and http://rebol.com/docs/view-guide.html for more information. Here's a little game that demonstrates common GUI techniques (this whole program was presented earlier in the demo app, in a more compact format without any comments. It's tiny.): ; Create a GUI that's centered on the user's screen: view center-face layout [ ; Define some basic layout parameters. "origin 0x0" ; starts the layout in the upper left corner of the ; GUI window. "space 0x0" dictates that there's no ; space between adjacent widgets, and "across" lays ; out consecutive widgets next to each other: origin 0x0 space 0x0 across ; The section below creates a newly defined button ; style called "piece", with an action block that ; swaps the current button's position with that of ; the adjacent empty space. That action is run ; whenever one of the buttons is clicked: style piece button 60x60 [ ; The line below checks to see if the clicked button ; is adjacent to the empty space. The "offset" ; refinement contains the position of the given ; widget. The word "face" is used to refer to the ; currently clicked widget. The "empty" button is ; defined later (at the end of the GUI layout). ; It's ok that the empty button is not yet defined, ; because this code is not evaluated until the ; the entire layout is built and "view"ed: if not find [0x60 60x0 0x-60 -60x0 ] (face/offset - empty/offset) [exit] ; In English, that reads 'subtract the position of ; the empty space from the position of the clicked ; button (the positions are in the form of ; Horizontal x Vertical coordinate pairs). If that ; difference isn't 60 pixels on one of the 4 sides, ; then don't do anything.' (60 pixels is the size of ; the "piece" button defined above.) ; The next three lines swap the positions of the ; clicked button with the empty button. ; First, create a variable to hold the current ; position of the clicked button: temp: face/offset ; Next, move the button's position to that of the ; current empty space: face/offset: empty/offset ; Last, move the empty space (button), to the old ; position occupied by the clicked button: empty/offset: temp ] ; The lines below draw the "piece" style buttons onto ; the GUI display. Each of these buttons contains all ; of the action code defined for the piece style above: piece "1" piece "2" piece "3" piece "4" return piece "5" piece "6" piece "7" piece "8" return piece "9" piece "10" piece "11" piece "12" return piece "13" piece "14" piece "15" ; Here's the empty space. Its beveled edge is removed ; to make it look less like a movable piece, and more ; like an empty space: empty: piece 200.200.200 edge [size: 0] ] Advanced users may be interested in understanding why the two words "view" and "layout" are used to create GUIs. Those functions represent two complete and separate language dialects in REBOL. The "view" function is a front end to the lower level graphic compositing engine and user interface system built into REBOL. "Layout" is a higher level function that simply assembles view functions required to draw and manipulate common GUI elements. Understanding how the two operate under the hood is helpful in understanding just how deep, compact, and powerful the REBOL language and dialecting design is. For more information, see http://rebol.com/docs/view-system.html.

6.4 Blocks, Series, and Strings

In REBOL, all multiple pieces of grouped data items are stored in "blocks". Blocks are delineated by starting and ending brackets: [ ] Data items in blocks are separated by white space. Here's a block of text items: ["John" "Bill" "Tom" "Mike"] Blocks were snuck in earlier as multiple text arguments passed to the "rejoin" function, and as brackets used to delineate GUI code passed to the 'view layout' functions: rejoin ["Hello " "there!"] view layout [button "Click Me" [alert "Hello there!"]] Blocks are actually the fundamental structure used to organize REBOL code. You'll find brackets throughout the language syntax to delineate functions, parameters, and other items. In the next section of this tutorial, you'll see more about functions and control structures that use brackets to separate grouped items of code. This section will cover how data can be grouped into blocks. The key concept to understand with blocks is that they are used to hold multiple pieces of data. Like any other variable data, blocks can be assigned word labels: some-names: ["John" "Bill" "Tom" "Mike"] ; "some-names" now refers to all 4 of those text items print some-names Blocks of text data (lists) can be displayed in GUIs, using the "text-list" widget: view layout [text-list data (some-names)] The "append" function is used to add items to a block: append some-names "Lee" print some-names append gui-layout1 [text "This text was appended to the GUI block."] view layout gui-layout1 The "foreach" function is used to do something to/with each item in a block: foreach item some-names [alert item] The "remove-each" function can be used to remove items from a block that match a certain criteria: remove-each name some-names [find name "i"] ; removes all names containing the letter "i" - returns ["John" "Tom"] Empty data blocks are created with the "copy" function. "Copy" assures that blocks are erased and defined without any previous content. You'll use "copy" whenever you need to create an empty block: ; Create a new empty block like this: empty-block: copy [] ; NOT like this: empty-block: [] Here's a very typical example that uses a block to save text entered into the fields of a GUI. When the "Save" button is pressed, the text in each of the fields is appended to a new empty block, then that whole block is saved to a text file. To later retrieve the saved values, the block is loaded from the text file, and its items assigned back to the appropriate fields in the GUI: view gui: layout [ ; label some text fields: field1: field field2: field field3: field ; add a button: btn "Save" [ ; when the button is clicked, create a new empty block: save-block: copy [] ; add the text contained in each field to the block: append save-block field1/text append save-block field2/text append save-block field3/text ; save the block to a file: save %save.txt save-block alert {SAVED -- Now try running this script again, and load the data back into the fields.} ] ; another button: btn "Load" [ ; load the saved block: save-block: load %save.txt ; set the text in each field to the contents of the block: field1/text: save-block/1 field2/text: save-block/2 field3/text: save-block/3 ; update the GUI display: show gui ] ] After running the script above, open the save.txt file with a text editor, and you'll see it contains the text from the fields in the GUI. You can edit the save.txt file with your text editor, then click the "Load" button, and the edited values will appear back in the GUI. You'll use blocks regularly to store and retrieve multiple pieces of data in this way, using text files. 6.4.1 Series Functions In REBOL, blocks can be automatically treated as lists of data, called "series", and manipulated using built-in functions that enable searching, sorting, and otherwise organizing the blocked data: some-names: ["John" "Bill" "Tom" "Mike"] sortednames: sort some-names ; sort alphabetically/ordinally print first sortednames ; displays the first item ("Bill") print sortednames/1 ; ALSO displays the first item ("Bill") ; (just an alternate syntax) print pick sortednames 1 ; ALSO displays the first item ("Bill") ; (another alternate syntax) find some-names "John" ; SEARCH for "John" in the block, ; set a position marker after that ; item - a very important function find/last some-names "John" ; search for "John" backwards from ; the end of the block select some-names "John" ; search for "John" in the block ; and return the Next item. reverse sortednames ; reverse the order of items in the ; block length? sortednames ; COUNT items in the block - important head sortednames ; set a position marker at the ; beginning of the block next sortednames ; set a position marker at the next ; item in the block back sortednames ; set a position marker at the ; previous item in the block last sortednames ; set a position marker at the last ; item in the block tail sortednames ; set a position marker after the ; last item in the block at sortednames x ; set a position marker at the x ; numbered item in the block skip sortednames x ; set a position marker x items ; forward or backward in the block extract sortednames 3 ; collect every third item from the ; block index? sortednames ; retrieves position number of the ; currently marked item in the block insert sortednames "Lee" ; add the name "Lee" at the current ; position in the block append sortednames "George" ; add "George" to the tail of the block ; and set position marker to the head remove sortednames ; remove the item at the currently ; marked position in the block remove find sortednames "Mike" ; ... find the "Mike" item in the ; block and remove it change sortednames "Phil" ; change the item at the currently ; marked position to "Phil" change third sortednames "Phil" ; change the third item to "Phil" poke sortednames 3 "Phil" ; another way to change the third item ; to "Phil" copy/part sortednames 2 ; get the first 2 items in the block clear sortednames ; remove all items in the block after ; the currently marked position replace/all sortednames "Lee" "Al" ; replace all occurrences of "Lee" in ; the block with "Al" both: join some-names sortednames ; concatenate both blocks together intersect sortednames some-names ; returns the items found in both ; blocks difference sortednames some-names ; returns the items that are NOT ; found in BOTH blocks exclude sortednames some-names ; returns the items in sortednames that ; are NOT also in some-names union sortednames some-names ; returns the items found in both ; blocks, ignoring duplicates unique sortednames ; returns all items in the block, ; with duplicates removed empty? sortednames ; returns true if the block is empty write %/c/names.txt some-names ; write the block to the hard drive ; as raw text data save %/c/namess.txt some-names ; write the block to the hard drive ; as native REBOL formatted code Learning to use series functions is absolutely fundamental to using REBOL. They will be covered by example throughout this text. See http://www.rebol.com/docs/dictionary.html for a list of additional series functions. For more information and examples, be sure to read sections 6 and 7 from the REBOL/Core Users Guide by Carl Sassenrath. 6.4.2 REBOL Strings In REBOL, a "string" is simply a series of characters. If you have experience with other programming languages, this can be one of the sticking points in learning REBOL. REBOL's solution is actually a very powerful, easy to learn and consistent with the way other operations work in the language. Proper string management simply requires a good understanding of series. Take a look at the following examples to see how to do a few common operations: the-string: "abcdefghijklmnopqrstuvwxyz" ; Left String: (get the left 7 characters of the string): copy/part the-string 7 ; Right String: (Get the right 7 characters of the string): copy at tail the-string -7 ; Mid String 1: (get 7 characters from the middle of the string, ; starting with the 12th character): copy/part (at the-string 12) 7 ; Mid String 2: (get 7 characters from the middle of the string, ; starting 7 characters back from the letter "m"): copy/part (find the-string "m") -7 ; Mid String 3: (get 7 characters from the middle of the string, ; starting 12 characters back from the letter "t"): copy/part (skip (find the-string "t") -12) 7 ; 3 different ways to get just the 7th character: the-string/7 pick the-string 7 seventh the-string ; Change "cde" to "123" replace the-string "cde" "123" ; Several ways to change the 7th character to "7" change (at the-string 7) "7" poke the-string 7 #"7" ; the pound symbol refers to a single character poke the-string 7 (to-char "7") ; another way to use single characters print the-string ; Remove 15 characters, starting at the 3rd position: remove/part (at the-string 3) 15 print the-string ; Insert 15 characters, starting at the 3rd position: insert (at the-string 3) "cdefghijklmnopq" print the-string ; Insert 3 instances of "-+" at the beginning of the string: insert/dup head the-string "-+ " 3 print the-string ; Replace every instance of "-+ " with " ": replace/all the-string "-+ " " " print the-string ; Remove spaces from a string (type "? trim" to see all its refinements!): trim the-string print the-string ; Get every third character from the string: extract the-string 3 ; Get the ASCII value for "c" (ASCII 99): to-integer third the-string ; Get the character for ASCII 99 ("c"): to-char 99 ; Convert the above character value to a string value: to-string to-char 99 ; Convert any value to a string: to-string now to-string $2344.44 to-string to-char 99 to-string system/locale/months ; An even better way to convert values to strings: form now form $2344.44 form to-char 99 form system/locale/months ; convert blocks to nicely formed strings ; Covert strings to a block of characters: the-block: copy [] foreach item the-string [append the-block item] probe the-block REBOL's series functions are very versatile. Often, you can devise several ways to do the same thing: ; Remove the last part of a URL: the-url: "http://website.com/path" clear at the-url (index? find/last the-url "/") print the-url ; Another way to do it: the-url: "http://website.com/path" print copy/part the-url (length? the-url)-(length? find/last the-url "/") (Of course, REBOL has a built-in helper function to accomplish the above goal, directly with URLs): the-url: http://website.com/path print first split-path the-url There are a number of additional functions that can be used to work specifically with string series. Run the following script for an introduction: string-funcs: [ build-tag checksum clean-path compress debase decode-cgi decompress dehex detab dirize enbase entab import-email lowercase mold parse-xml reform rejoin remold split-path suffix? uppercase ] echo %string-help.txt ; "echo" saves console activity to a file foreach word string-funcs [ print "___________________________________________________________^/" print rejoin ["word: " uppercase to-string word] print "" do compose [help (to-word word)] ] echo off editor at read %string-help.txt 4 See http://www.rebol.com/docs/dictionary.html and http://rebol.com/docs/core23/rebolcore-8.html for more information about the above functions. 6.4.3 Indentation Blocks often contain other blocks. Such compound blocks are typically indented with consecutive tab stops. Starting and ending brackets are normally placed at the same indentation level. This is conventional in most programming languages, because it makes complex code easier to read, by grouping things visually. For example, the compound block below: big-block: [[may june july] [[1 2 3] [[yes no] [monday tuesday friday]]]] can be written as follows to show the beginnings and endings of blocks more clearly: big-block: [ [may june july] [ [1 2 3] [ [yes no] [monday tuesday friday] ] ] ] probe first big-block probe second big-block probe first second big-block probe second second big-block probe first second second big-block probe second second second big-block Indentation is not required, but it's very helpful. 6.4.4 More About Why/How Blocks are Useful IMPORTANT: In REBOL, blocks can contain mixed data of ANY type (text and binary items, embedded lists of items (other blocks), variables, etc.): some-items: ["item1" "item2" "item3" "item4"] an-image: load http://rebol.com/view/bay.jpg append some-items an-image ; "some-items" now contains 4 text strings, and an image! ; You can save that entire block of data, INCUDING THE BINARY ; IMAGE data, to your hard drive as a SIMPLE TEXT FILE: save/all %some-items.txt some-items ; to load it back and use it later: some-items: load %some-items.txt view layout [image fifth some-items] Take a moment to examine the example above. REBOL's block structure works in a way that is dramatically easy to use compared to other languages and data management solutions (much more simply than most database systems). It's is a very flexible, simple, and powerful way to store data in code! The fact that blocks can hold all types of data using one simple syntactic structure is a fundamental reason it's easier to use than other programming languages and computing tools. You can save/load block code to the hard drive as a simple text file, send it in an email, display it in a GUI, compress it and transfer it to a web server to be downloaded by others, transfer it directly over a point-to-point network connection, or even convert it to XML, encrypt, and store parts of it in a secure multiuser database to be accessed by other programming languages, etc... Remember, all programming, and computing in general, is essentially about storing, organizing, manipulating, and transferring data of some sort. REBOL makes working with all types of data very easy - just put any number of pieces of data, of any type, in between two brackets, and that data is automatically searchable, sortable, storable, transferable, and otherwise usable in your programs. 6.4.5 Evaluating Variables in Blocks: Compose, Reduce, Pick and More You will often find that you want to refer to an item in a block by its index (position number), as in the earlier 'some-items' example: view layout [image some-items/5] You may not, however, always know the specific index number of the data item you want to access. For example, as you insert data items into a block, the index position of the last item changes (it increases). You can obtain the index number of the last item in a block simply by determining the number of items in the block (the position number of the last item in a block is always the same as the total number of items in the block). In the example below, that index number is assigned the variable word "last-item": last-item: length? some-items Now you can use that variable to pick out the last item: view layout [image (pick some-items last-item)] ; In our earlier example, with 5 items in the block, the ; line above evaluates the same as: view layout [image (pick some-items 5)] You can refer to other items by adding and subtracting index numbers: alert pick some-items (last-item - 4) There are several other ways to do the exact same thing in REBOL. The "compose" function allows variables in parentheses to be evaluated and inserted as if they'd been typed explicitly into a code block: view layout compose [image some-items/(last-item)] ; The line above appears to the interpreter as if the following ; had been typed: view layout [image some-items/5] The "compose" function is very useful whenever you want to refer to data at variable index positions within a block. The "reduce" function can also be used to produce the same type of evaluation. Function words in a reduced block should begin with the tick (') symbol: view layout reduce ['image some-items/(last-item)] Another way to use variable values explicitly is with the ":" format below. This code evaluates the same as the previous two examples: view layout [image some-items/:last-item] Think of the colon format above as the opposite of setting a variable. As you've seen, the colon symbol placed after a variable word sets the word to equal some value. A colon symbol placed before a variable word gets the value assigned to the variable, and inserts that value into the code as if it had been typed explicitly. You can use the "index?" and "find" functions to determine the index position(s) of any data you're searching for in a block: index-num: index? (find some-items "item4") Any of the previous 4 formats can be used to select the data at the determined variable position: print pick some-items index-num print compose [some-items/(index-num)] print reduce [some-items/(index-num)] ; no function words are used in the block above, so no ticks are required print some-items/:index-num Here's an example that displays variable image data contained in a block, using a foreach loop. The "compose" function is used to include dynamically changeable data (image representations), as if that data had been typed directly into the code: photo1: load http://rebol.com/view/bay.jpg photo2: load http://rebol.com/view/demos/palms.jpg ; The REBOL interpreter sees the following line as if all the code ; representing the above images had been typed directly in the block: photo-block: compose [(photo1) (photo2)] foreach photo photo-block [view layout [image photo]] Block concepts may seem a bit vague at this point. The practical application of block structures will be presented much more thoroughly, by example, throughout this tutorial, and clarification about the usefulness of blocks will come from seeing them in working code. For additional detailed information about using blocks and series functions see http://www.rebol.com/docs/core23/rebolcore-6.html.

6.5 Conditions

6.5.1 If Conditions are used to manage program flow. The most basic conditional evaluation is "if": if (this expression is true) [do this block of code] ; parentheses are not required Math operators are typically used to perform conditional evaluations: = < > <> (equal, less-than, greater-than, not-equal): if now/time > 12:00 [alert "It's after noon."] Here's an example that gets a username and password from the user, tests that data using an "if" evaluation, and alerts the user if the response is correct: userpass: request-pass/title "Type 'username' and 'password'" if (userpass = ["username" "password"]) [alert "Welcome back!"] 6.5.2 Either "Either" is an if/then/else evaluation that chooses between two blocks to evaluate, based on whether the given condition is true or false. Its syntax is: either (condition) [ block to perform if the condition is true ][ block to perform if the condition is false ] For example: either now/time > 8:00am [ alert "It's time to get up!" ][ alert "You can keep on sleeping." ] userpass: request-pass either userpass = ["username" "password"] [ alert "Welcome back!" ][ alert "Incorrect user/password combination!" ] 6.5.3 Switch The "switch" evaluation chooses between numerous functions to perform, based on multiple evaluations. Its syntax is: switch/default (main value) [ (value 1) [block to execute if value 1 = main value (value 2) [block to execute if value 2 = main value] (value 3) [block to execute if value 3 = main value] ; etc... ] [default block of code to execute if none of the values match] You can compare as many values as you want against the main value, and run a block of code for each matching value: favorite-day: request-text/title "What's your favorite day of the week?" switch/default favorite-day [ "Monday" [alert "Monday is the worst! The work week begins..."] "Tuesday" [alert "Tuesdays and Thursdays are both ok, I guess..."] "Wednesday" [alert "The hump day - the week is halfway over!"] "Thursday" [alert "Tuesdays and Thursdays are both ok, I guess..."] "Friday" [alert "Yay! TGIF!"] "Saturday" [alert "Of course, the weekend!"] "Sunday" [alert "Of course, the weekend!"] ] [alert "You didn't type in the name of a day!"] 6.5.4 Case You can choose between multiple evaluations of any complexity using the "case" structure. If none of the cases evaluate to true, you can use any true value to trigger a default evaluation: name: "john" case [ find name "a" [print {Your name contains the letter "a"}] find name "e" [print {Your name contains the letter "e"}] find name "i" [print {Your name contains the letter "i"}] find name "o" [print {Your name contains the letter "o"}] find name "u" [print {Your name contains the letter "u"}] true [print {Your name doesn't contain any vowels!}] ] for i 1 100 1 [ case [ (0 = modulo i 3) and (0 = modulo i 5) [print "fizzbuzz"] 0 = modulo i 3 [print "fizz"] 0 = modulo i 5 [print "buzz"] true [print i] ] ] By default, the case evaluation automatically exits once a true evaluation is found (i.e., in the name example above, if the name contains more than one vowel, only the first vowel will be printed). To check all possible cases before ending the evaluation, use the /all refinement: name: "brian" found: false case/all [ find name "a" [print {Your name contains the letter "a"} found: true] find name "e" [print {Your name contains the letter "e"} found: true] find name "i" [print {Your name contains the letter "i"} found: true] find name "o" [print {Your name contains the letter "o"} found: true] find name "u" [print {Your name contains the letter "u"} found: true] found = false [print {Your name doesn't contain any vowels!}] ] 6.5.5 Multiple Conditions: "and", "or", "all", "any" You can check for more than one condition to be true, using the "and", "or", "all", and "any" words: ; first set some initial values all to be true: value1: value2: value3: true ; then set some additional values all to be false: value4: value5: value6: false ; The following prints "both true", because both the first ; condition AND the second condition are true: either ( (value1 = true) and (value2 = true) ) [ print "both true" ] [ print "not both true" ] ; The following prints "both not true", because the second ; condition is false: either ( (value1 = true) and (value4 = true) ) [ print "both true" ] [ print "not both true" ] ; The following prints "either one OR the other is true" ; because the first condition is true: either ( (value1 = true) or (value4 = true) ) [ print "either one OR the other is true" ] [ print "neither is true" ] ; The following prints "either one OR the other is true" ; because the second condition is true: either ( (value4 = true) or (value1 = true) ) [ print "either one OR the other is true" ] [ print "neither is true" ] ; The following prints "either one OR the other is true" ; because both conditions are true: either ( (value1 = true) or (value4 = true) ) [ print "either one OR the other is true" ] [ print "neither is true" ] ; The following prints "neither is true": either ( (value4 = true) or (value5 = true) ) [ print "either one OR the other is true" ] [ print "neither is true" ] For comparisons involving more items, you can use "any" and "all": ; The following lines both print "yes", because ALL comparisons are true. ; "All" is just shorthand for the multiple "and" evaluations: if ((value1 = true) and (value2 = true) and (value3 = true)) [ print "yes" ] if all [value1 = true value2 = true value3 = true] [ print "yes" ] ; The following lines both print "yes" because ANY ONE of the comparisons ; is true. "Any" is just shorthand for the multiple "or" evaluations: if ((value1 = true) or (value4 = true) or (value5 = true)) [ print "yes" ] if any [value1 = true value4 = true value5 = true] [ print "yes" ]

6.6 Loops

6.6.1 Forever "Loop" structures provide programmatic ways to methodically repeat actions, manage program flow, and automate lengthy data processing activities. The "forever" function creates a simple repeating loop. Its syntax is: forever [block of actions to repeat] The following code uses a forever loop to continually check the time. It alerts the user when 60 seconds has passed. Notice the "break" function, used to stop the loop: alarm-time: now/time + :00:60 forever [if now/time = alarm-time [alert "1 minute has passed" break]] Here's a more interactive version using some info provided by the user. Notice how the forever loop, if evaluation, and alert arguments are indented to clarify the grouping of related parameters: event-name: request-text/title "What do you want to be reminded of?" seconds: to-integer request-text/title "Seconds to wait?" alert rejoin [ "It's now " now/time ", and you'll be alerted in " seconds " seconds." ] alarm-time: now/time + seconds forever [ if now/time = alarm-time [ alert rejoin [ "It's now "alarm-time ", and " seconds " seconds have passed. It's time for: " event-name ] break ] ] Here's a forever loop that displays/updates the current time in a GUI: view layout [ timer: field button "Start" [ forever [ set-face timer now/time wait 1 ] ] ] 6.6.2 Loop The "loop" function allows you to repeatedly evaluate a block of code, a specified number of times: loop 50 [print "REBOL is great!"] 6.6.3 Repeat Like "loop", the "repeat" function allows you to repeatedly evaluate a block of code, a specified number of times. It additionally allows you to specify a counter variable which is automatically incremented each time through the loop: repeat count 50 [print rejoin ["This is loop #: " count]] The above code does the same thing as: count: 0 loop 50 [ count: count + 1 print rejoin ["This is loop #: " count] ] 6.6.4 For "For" loops allow you to control repetition patterns that involve consecutively changing values. You specify a start value, end value, incremental value, and a variable name to hold the current value during the loop. Here's the "for" loop syntax: for {variable word to hold current value} {starting value} {ending value} {incremental value} [block of code to perform, which can use the current variable value] For example: for counter 1 10 1 [print counter] ; starts on 1 and counts to 10 by increments of 1 for counter 10 1 -1 [print counter] ; starts on 10 and counts backwards to 1 by increments of -1 for counter 10 100 10 [print counter] ; starts on 10 and counts to 100 by increments of 10 for counter 1 5 .5 [print counter] ; starts on 1 and counts to 5 by increments of .5 for timer 8:00 9:00 0:05 [print timer] ; starts at 8:00am and counts to 9:00am by increments of 5 minutes for dimes $0.00 $1.00 $0.10 [print dimes] ; starts at 0 cents and counts to 1 dollar by increments of a dime for date 1-dec-2005 25-jan-2006 8 [print date] ; starts at December 12, 2005 and counts to January 25, 2006 ; and by increments of 8 days for alphabet #"a" #"z" 1 [prin alphabet] ; starts at the character a and counts to z by increments of 1 letter Notice that REBOL properly increments dates, money, time, etc. This "for" loop displays the first 5 file names in the current folder on your hard drive: files: read %. for count 1 5 1 compose [print files/(count)] Notice the "compose" word used in the for loop. "files/1" represents the first item in the file list, "files/2" represents the second, and so on. The first time though the loop, the code reads as if [print files/1] had been typed in manually, etc. The following "for" loop displays all the files in the current folder: files: read %. filecount: length? files for count 1 filecount 1 compose [print files/(count)] 6.6.5 Foreach (very important!) The "foreach" function lets you easily loop through a block of data. Its syntax is: foreach {variable name referring to each consecutive item in the given block} [given block] [block of functions to be executed upon each item in the given block, using the variable name to refer to each successive item] This example prints the name of every file in the current directory on your hard drive: folder: read %. foreach file folder [print file] This line reads and prints each successive message in a user's email box: foreach mail (read pop://user:pass@website.com) [print mail] Here's a slightly more complex foreach example: ; define a block of text items: some-names: ["John" "Bill" "Tom" "Mike"] ; define a variable used to count items in the block: count: 0 ; go through each item in the block: foreach name some-names [ ; increase the counter variable by 1, for each item: count: count + 1 ; print the count number, and the associated text item: print rejoin ["Item " count ": " name] ] Here's an example in which an empty block is created and data is appended using a foreach loop. The data is then converted to a text string and displayed in a GUI: ; define a block of text items: some-names: ["John" "Bill" "Tom" "Mike"] ; create another new, empty block: data-block: copy [] ; define a variable used to count items in the block: count: 0 ; go through each item in the block: foreach name some-names [ ; increase the counter variable by 1, for each item: count: count + 1 ; for each item, add some rejoined text to the originally empty block: append data-block rejoin ["Item " count ": " name newline] ] ; convert the newly created block to a string, and show it in a ; GUI text area widget: view layout [area (to-string data-block)] You can select multiple values from a block during each iteration of a foreach loop, using the following format: users: [ "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" ] ; Use the following format to get 3 consecutive values from the above ; block, each time through the loop: foreach [name address phone] users [ print rejoin [ "^/Name: " name ; gets 1 value from the block "^/Address: " address ; gets the next value from the block "^/Phone: " phone ; gets a third value from the block ] ] You will use the foreach function very often in REBOL code. It will be demonstrated many times, by example, throughout this tutorial. 6.6.6 Forall and Forskip "Forall" loops through a block, incrementing the marked index number of the series as it loops through: some-names: ["John" "Bill" "Tom" "Mike"] foreach name some-names [print index? some-names] ; index doesn't change forall some-names [print index? some-names] ; index changes foreach name some-names [print name] forall some-names [print first some-names] ; same effect as line above "Forskip" works like forall, but skips through the block, jumping a periodic number of elements on each loop: some-names: ["John" "Bill" "Tom" "Mike"] forskip some-names 2 [print first some-names] 6.6.7 While and Until The "while" function repeatedly evaluates a block of code while the given condition is true. While loops are formatted as follows: while [condition] [ block of functions to be executed while the condition is true ] This example counts to 5: x: 1 ; create an initial counter value while [x <= 5] [ alert to-string x x: x + 1 ] In English, that code reads: "x" initially equals 1. While x is less than or equal to 5, display the value of x, then add 1 to the value of x and repeat. Some additional "while" loop examples: while [not request "End the program now?"] [ alert "Select YES to end the program." ] ; "not" reverses the value of data received from ; the user (i.e., yes becomes no and visa versa) alert "Please select today's date" while [request-date <> now/date] [ alert rejoin ["Please select TODAY's date. It's " now/date] ] while [request-pass <> ["username" "password"]] [ alert "The username is 'username' and the password is 'password'" ] "Until" loops are similar to "while" loops. They do everything in a given block, repeatedly, until the last expression in the block evaluates to true: x: 10 until [ print rejoin ["Counting down: " x] x: x - 1 x = 0 ] The example below uses several loops to alert the user to feed the cat, every 6 hours between 8am and 8pm. It uses a for loop to increment the times to be alerted, a while loop to continually compare the incremented times with the current time, and a forever loop to do the same thing every day, continuously. Notice the indentation: forever [ for timer 8:00am 8:00pm 6:00 [ while [now/time <= timer] [wait :00:01] alert rejoin ["It's now " now/time ". Time to feed the cat."] ] ]

6.7 User Defined Functions and Imported Code

REBOL's built-in functions satisfy many fundamental needs. To achieve more complex or specific computations, you can create your own function definitions. Data and function words contained in blocks can be evaluated (their actions performed and their data values assigned) using the "do" word. Because of this, any block of code can essentially be treated as a function. That's a powerful key element of the REBOL language design: some-actions: [ alert "Here is one action." print "Here's a second action." write %/c/anotheraction.txt "Here's a third action." ] do some-actions New function words can also be defined using the "does" and "func" words. "Does" is included directly after a word label definition, and forces a block to be evaluated every time the word is encountered: more-actions: does [ alert "4" alert "5" alert "6" ] ; now to use that function, just type the word label: more-actions Here's a useful function to clear the command line screen in the REBOL interpreter. cls: does [prin "^(1B)[J"] cls The "func" word creates an executable block in the same way as "does", but additionally allows you to pass your own specified parameters to the newly defined function word. The first block in a func definition contains the name(s) of the variable(s) to be passed. The second block contains the actions to be taken. Here's the "func" syntax: func [names of variable(s) to be passed] [ actions to be taken with those variables ] This function definition: sqr-add-var: func [num1 num2] [print square-root (num1 + num2)] Can be used as follows. Notice that no brackets, braces, or parentheses are required to contain the data arguments. Data parameters simply follow the function word, on the same line of code: sqr-add-var 12 4 ; prints "4", the square root of 12 + 4 (16) sqr-add-var 96 48 ; prints "12", the square root of 96 + 48 (144) Here's a simple function to display images: display: func [filename] [view layout [image load to-file filename]] display (request-file) By default, the last value evaluated by a function is returned when the function is complete: concatenate: func [string1 string2] [join string1 string2] string3: concatenate "Hello " "there." print string3 By default, values used inside functions are treated as global, which means that if any variables are changed inside a function, they will be changed throughout the rest of your program: x: 10 change-x-globally: func [y z] [x: y + z] change-x-globally 10 20 print x You can change this default behavior, and specify that any value be treated as local to the function (not changed throughout the rest of your program), by using the "/local" refinement: x: 10 change-x-locally: func [y z /local x] [x: y + z] change-x-locally 10 20 ; inside the function, x is now 30 print x ; outside the function, x is still 10 You can specify refinements to the way a function operates, simply by preceding optional operation arguments with a forward slash ("/"): compute: func [x y /multiply /divide /subtract] [ if multiply [return x * y] if divide [return x / y] if subtract [return x - y] return x + y ] compute/multiply 10 20 compute/divide 10 20 compute/subtract 10 20 compute 10 20 The "help" function provides usage information for any function, including user defined functions: help for help compute You can include documentation for any user defined function by including a text string as the first item in it's argument list. This text is included in the description displayed by the help function: doc-demo: func ["This function demonstrates doc strings"] [help doc-demo] doc-demo Acceptable data types for any parameter can be listed in a block, and doc strings can also be included immediately after any parameter: concatenate-string-or-num: func [ "This function will only concatenate strings or integers." val1 [string! integer!] "First string or integer" val2 [string! integer!] "Second string or integer" ] [ join val1 val2 ] help concatenate-string-or-num concatenate-string-or-num "Hello " "there." ; this works correctly concatenate-string-or-num 10 20 ; this works correctly concatenate-string-or-num 10.1 20.3 ; this creates an error 6.7.1 Importing Code You can "do" a module of code contained in any text file, as long as it contains the minimum header "REBOL [ ]" (this includes HTML files and any other files that can be read via REBOL's built-in protocols). For example, if you save the previous functions in a text file called "myfunctions.r": REBOL [] ; THIS HEADER TEXT MUST BE INCLUDED AT THE TOP OF ANY REBOL FILE sqr-add-var: func [num1 num2] [print square-root (num1 + num2)] display: func [filename] [view layout [image load filename]] cls: does [prin "^(1B)[J"] You can import and use them in your current code, as follows: do %myfunctions.r ; now you can use those functions just as you would any other ; native function: sqr-add-var display cls Here's an example function that plays a .wave sound file. Save this code as C:\play_sound.r: REBOL [title: "play-sound"] ; you can add a title to the header play-sound: func [sound-file] [ wait 0 ring: load sound-file sound-port: open sound:// insert sound-port ring wait sound-port close sound-port ] Then run the code below to import the function and play selected .wav files: do %/c/play_sound.r play-sound %/C/WINDOWS/Media/chimes.wav play-sound to-file request-file/file %/C/WINDOWS/Media/tada.wav Imported files can contain data definitions and any other executable code, including that which is contained in additional nested source files imported with the "do" function. Any code or data contained in a source file is evaluated when the file is "do"ne.

6.8 Quick Review and Synopsis

The list below summarizes some key characteristics of the REBOL language. Knowing how to put these elements to use constitutes a fundamental understanding of how REBOL works: To start off, REBOL has hundreds of built-in function words that perform common tasks. As in other languages, function words are typically followed by passed parameters. Unlike other languages, passed parameters are placed immediately after the function word and are not necessarily enclosed in parenthesis. To accomplish a desired goal, functions are arranged in succession, one after another. The value(s) returned by one function are often used as the argument(s) input to another function. Line terminators are not required at any point, and all expressions are evaluated in left to right order, then vertically down through the code. Empty white space (spaces, tabs, newlines, etc.) can be inserted as desired to make code more readable. Text after a semicolon and before a new line is treated as a comment. You can complete significant work by simply knowing the predefined functions in the language, and organizing them into a useful order. REBOL contains a rich set of conditional and looping structures, which can be used to manage program flow and data processing activities. If, switch, while, for, foreach, and other typical structures are supported. Because many common types of data values are automatically recognized and handled natively by REBOL, calculating, looping, and making conditional decisions based upon data content is straightforward and natural to perform, without any external modules or toolkits. Numbers, text strings, money values, times, tuples, URLs, binary representations of images, sounds, etc. are all automatically handled. REBOL can increment, compare, and perform proper computations on most common types of data (i.e., the interpreter automatically knows that 5:32am + 00:35:15 = 6:07:15am, and it can automatically apply visual effects to raw binary image data, etc.). Network resources and Internet protocols (http documents, ftp directories, email accounts, dns services, etc.) can also be accessed natively, just as easily as local files. Data of any type can be written to and read from virtually any connected device or resource (i.e., "write %file.txt data" works just as easily as "write ftp://user:pass@website.com data", using the same common syntax). The percent symbol ("%") and the syntax "%(/drive)/path/path/.../file.ext" are used cross-platform to refer to local file values on any operating system. Any data or code can be assigned a word label. The colon character (":") is used to assign word labels to constants, variable values, evaluated expressions, functions, and data/action blocks of any type. Once assigned, variable words can be used to represent all of the data and/or actions contained in the given expression, block, etc. Just put a colon at the end of a word, and thereafter it represents all the following actions and/or data. That forms a significant part of the REBOL language structure, and is the basis for it's flexible natural language dialecting abilities. Multiple pieces of data are stored in "blocks", which are delineated by starting and ending brackets ("[]"). Blocks can contain data of any type: groups of text strings, arrays of binary data, collections of actions (functions), other enclosed blocks, etc. Data items contained in blocks are separated by white space. Blocks can be automatically treated as lists of data, called "series", and manipulated using built-in functions that enable searching, sorting, ordering, and otherwise organizing the blocked data. Data and function words contained in blocks can be evaluated (their actions performed and their data values assigned) using the "do" word. New function words can also be defined using the "does" and "func" words. "Does" forces a block to be evaluated every time its word label is encountered. The "func" word creates an executable block in the same way as "does", but additionally allows you to pass your own specified parameters to the newly defined function word. You can "do" a module of code contained in a text file, as long as it contains the minimum header "rebol[]". Blocks are also used to delineate most of the syntactic structures in REBOL (i.e., in conditional evaluations, function definitions, etc.). The syntax "view layout [block]" is used to create basic GUI layouts. You can add graphic widgets to the layout simply by adding widget identifier words to the enclosed block: "button", "field", "text-list", etc. Color, position, spacing, and other fa