REBOL Modules

    REBOL Enhancement Proposal: REP002
    Version: 1.0.1
    Author: Carl Sassenrath

Contents:

1. Overview
2. Context Binding
3. Creating Modules
4. Module Hierarchy
5. Module Reflection

1. Overview

A REBOL module is an independent context - a namespace. Modules are similar to objects in that they associate variables with values. However, modules extend beyond objects in the following ways:

Also note that like objects, modules can be nested to allow nested contexts.

Modules should not be confused with components, which are the plug-in feature-sets of REBOL. However, a component may include one or more modules.


2. Context Binding

The module design allows for varying degrees of context binding. You can control how free words (non-locals) are bound.

StrictBind every word to the local frame. The binder will extend the local frame as much as necessary to hold all words. The module is completely encapsulated.
UserEvery word that is not defined in a previous context will be bound to the local context. This allows users to get to REBOL's predefined words but have all new words bound to the local context. This is how most scripts will operate by default.
ExplicitAn explicit list of local words is provided for the module. All other free words will be bound to previous frames.
ImpliedWords that use SET notation at the top block level of the module will be local. All other are bound above. This is the same as object instance variables. This is the default for the MAKE module function.

3. Creating Modules

Modules can be created with either the MAKE function or by evaluating scripts that contain module words in their headers.

To create a module with MAKE, you must supply a module interface specification block and a body block. The MAKE has the general form:

    new: make spec body
The value returned from MAKE is the new module and it can be assigned to a variable, passed to another function, or returned as the result of a function.

3.1. Interface Specification

The specification block is a header that is similar to that used with scripts. It contains a title, date, version, author, and other fields of a header. In addition, there are fields that are unique to modules:

exportProvides a block of words that define the variables that can be accessed externally from the module.
importProvides an optional block of imported words. When this block has been provided, no external words are bound within the module other than those that have been specified. This allows you to limit the module's access to REBOL functions.
localSpecifies an optional block that explicitly defines the local variables of the module. If this is not specified, then the local variables will be defined by their top level block set operations (as done with objects).
module-optionsspecial flags used to control the level of binding for the module. For instance, the LOCAL attribute forces free variables that are not bound in any frame to be bound in the module, not bound in parent contexts. The USER attribute makes the module a top level frame (like a user frame).
Here is an example of a simple module definition:

    example: make module! [
        title: "Example module"
        version: 1.0.0
        export: [do-it]
    ][
        data: [block of local data]
        do-it: does [probe data]
    ]
The do-it function can now be called with:

    example/do-it
But, you cannot access the data. This line would cause an error:

    example/data
    ** Script Error: Invalid path value: data
    ** Where: example/data

3.2. Scripts as Modules

For convenience, a script can also be defined as a module. This is done by specifying the module description as part of the script header:

    REBOL. [
        title: "Script Module"
        version: 1.0.0
        module: 'script-module
        export: [do-it]
    ]

    ...
When the script is evaluated, it will be evaluated as a module. All of its free words (globals) will be local to the module.

3.3. Variable Definitions

Within a module, variables are defined as module-local in the same way that objects define their instance variables. You can write:

    make module! [export [a]] [
        a: b: c: d: e: f: none
        ...
    ]
You can explicitly declare the variables that are local to a module (but you will also need to remember to maintain this list!):

    make module! [export [a] local [a b c d e f]] [
        ...
    ]
All such locals will be set to NONE when the module is created.

Of course, if the LOCAL module-option is used, then all variables will be bound locally other than those that are explicitly imported.


4. Module Hierarchy

The new module architecture allows an enhanced context structure for REBOL and its scripts. Scripts will no longer be bound by default to the global frame (main-frame). Instead, scripts will be bound to a USER frame that is created below the main frame. This allows entire scripts and all of their functions and data to be garbage collected if they are no longer required.

This changes the binding process for scripts. The binder looks locally for a word, then looks up the hierarchy for the name. If the word does not appear anywhere in the hierarchy, then the module frame is extended with the new word. Effectively, free variables become module local, rather than global.

The hierarchy of module contexts is now defined as:

    ROOT frame (natives, and C code references)
        SYSTEM frame (most of the mezz functions)
            USER frame (user script)
                MODULE frame (sub-scripts or make modules)
When the USER module-option is used with MAKE, a peer frame is created to a script. The new module is placed at the USER frame level, rather than at the MODULE level. This allows you to create scripts that evaluate scripts in pristine environments. (That is, in the same environment as your first script.)

    

5. Module Reflection

You can obtain the header specification of a module with the FIRST function. This will return the header as was used to define the module. This also allows you to reflectively determine what words are exported by a module. You can use this to automatically generate documentation about a module.

    foreach word get in first module 'export [
        print word
    ]
-End: Makespec 1.4.0