REBOL 3 Docs Guide Concepts Functions Datatypes Errors
  TOC < Back Next >   Updated: 8-Mar-2009 Edit History  

REBOL 3 Concepts: Functions: Evaluating Functions

Pending Revision

This document was written for R2 and has yet to be revised for R3.

The way function arguments are evaluated dictates the general order of words and values in the language. This section goes into more detail on how functions are evaluated.

Contents

Arguments

Functions receive arguments and return results. Most functions require one or more arguments; although, some functions, such as now (current date and time), do not require any arguments.

The arguments that are supplied to a function are processed by the interpreter and then passed to the function. Arguments are processed in the same way, regardless of the type of function called, be it a native function, operator, user-defined function, or otherwise. For example, the [bad-link:functions/send.txt] function expects two arguments:

friend: luke@rebol.com
message: "message in a bottle"

send friend message

The word friend is first evaluated and its value (''luke@rebol.com ) is provided as the first argument to [bad-link:functions/send.txt]. Next, the word message is evaluated, and its value becomes the second argument. Think of the values of the friend and message variables as being substituted into the line before [bad-link:functions/send.txt] is done:

send luke@rebol.com "message in a bottle"

If you provide too few arguments to a function, an error message is returned. For example, the send function expects two arguments and if you send one, an error is returned

send friend
** Script Error: send is missing its message argument.
** Where: send friend

If too many arguments are provided, the extra values are ignored.

send friend message "urgent"

In the previous example, [bad-link:functions/send.txt] already has two arguments, so the string, which is the third argument, is ignored. Notice that no error message occurs. In this case, there were no functions expecting the third argument. However, in some cases the third argument may belong to another function that was evaluated before [bad-link:functions/send.txt].

Arguments to a function are evaluated from left to right. This order is followed even when the arguments themselves are functions. For example, if you write:

send friend detab copy message

the second argument must be computed by evaluating the detab function and the copy function. The result of the copy will be passed to detab, and the result of detab will be passed to [bad-link:functions/send.txt]. In the previous example, the copy function is taking a single argument, the message, and returns a copy of it. The copied message is passed to the detab function, which removes the tab characters and returns the detabbed message, which is passed to the [bad-link:functions/send.txt] function. Notice how the results of functions flow from right to left as the expression is evaluated.

The evaluation that is happening here can be shown by using parentheses to clarify what is evaluated first. (However, the parentheses are not required, and actually slow down the evaluation slightly.)

send friend (detab (copy message))

The cascading effect of results passed to functions is quite useful. Here is an example that uses insert twice within the same expression:

file: %image
insert tail insert file %graphics/ %.jpg
print file
graphics/image.jpg

In the following example, a directory name and a suffix are added to the base file name. Parentheses can be used to clarify the order of evaluation:

insert (tail (insert file %graphics/)) %.jpg
A Note About Parentheses

Parentheses make good "training wheels" to get started in writing REBOL. However, it won't take long before you can shed this aid and write the expressions directly without the parentheses. Not using parentheses lets the interpreter evaluate expressions quicker.

Argument DataTypes

Functions usually require arguments of a specific datatype. For example, the first argument to the [bad-link:functions/send.txt] function can only be an email address or block of email addresses. Any other type of value will produce an error:

send 1234 "numbers"
** Script Error: send expected address argument of type: email block.
** Where: send 1234 "numbers"

In the previous example, the error message is telling you that the address argument of the [bad-link:functions/send.txt] function needs to be either an email address or a block.

A quick way to find out what types of arguments are accepted by a function is to type the following at the console prompt:

help send
USAGE:
    SEND address message /only /header header-obj
DESCRIPTION:
    Send a message to an address (or block of addresses)
    SEND is a function value.
ARGUMENTS:
    address -- An address or block of addresses (Type: email block)
    message -- Text of message. First line is subject. (Type: any)
REFINEMENTS:
    /only -- Send only one message to multiple addresses
    /header -- Supply your own custom header
        header-obj -- The header to use (Type: object)

The ARGUMENTS section indicates the datatype of each argument. Notice that the second argument can be of any datatype. So, it is valid to write:

send luke@rebol.com $1000.00

Refinements

A refinement specifies a variation in the normal evaluation of a function. Refinements also allow optional arguments to be provided. Refinements are available for both native and user-defined functions.

Refinements are specified by following the function name with a forward slash (/) and a refinement name. For instance:

copy/part  (copy just part of a string)

find/tail  (return the tail of the match)

load/markup  (return XML/HTML tags and strings)

Functions can also include multiple refinements:

find/case/tail (match case and return tail)

insert/only/dup (insert entire block multiple times)

You have seen the copy function used to make a copy of a string. By default, copy returns a copy of its argument:

string: "no time like the present"
print copy string
no time like the present

Using the /part refinement, copy returns part of the string:

print copy/part string 7
no time

In the previous example, the /part refinement specifies that only seven characters of the string are copied.

To review what refinements are allowed on a function such as copy, use online help:

help copy
USAGE:
    COPY value /part range /deep
DESCRIPTION:
     Returns a copy of a value.
     COPY is an action value.
ARGUMENTS:
     value -- Usually a series (Type: series port bitset)
REFINEMENTS:
     /part -- Limits to a given length or position.
         range -- (Type: number series port)
     /deep -- Also copies series values within the block.

Notice that the /part refinement requires an additional argument. Not all refinements require additional arguments. For example, the /deep refinement specifies that copy make copies of all its sub-blocks. No other arguments are required.

When multiple refinements are used with a function, the order of the extra arguments is determined by the order in which the refinements are specified. For example:

str: "test"
insert/dup/part str "this one" 4 5
print str
this this this this test

Reversing the order of the /dup and /part refinement changes the order of the arguments. You can see the difference:

str: "test"
insert/part/dup str "this one" 4 5
print str
thisthisthisthisthistest

The refinements indicate the order of the arguments.

Function Values

The previous examples describe how functions return values when they are evaluated. Sometimes, however, you want to obtain the function as a value, not the value it returns. This can be done by preceding the function name with a colon or using the get function. For example, to set a word, pr, to the print function, you would write:

pr: :print

You could also write:

pr: get `print

Now pr is equivalent to the print function:

pr "this is a test"
this is a test


  TOC < Back Next > REBOL.com - WIP Wiki Feedback Admin