REBOL 3.0

Modules: Free Variables (Globals) are Implied Locals

Carl Sassenrath, CTO
REBOL Technologies
6-Oct-2006 20:29 GMT

Article #0048
Main page || Index || Prior Article [0047] || Next Article [0049] || 19 Comments || Send feedback

The prior article talked about how making modules was trivial and provided an example.

Some of you REBOL experts may be wondering what happens in this case:

REBOL [
    title: "Stock Handler"
    module: 'mod-stocks
]

stocks: []

buy: func [symbol price] [
    last-stock: symbol
    repend stocks [symbol price]
]

sell: func [symbol price] [
    if not symbol [symbol: last-stock]
    remove-all [asymbol aprice] stocks [
        symbol = asymbol
    ]
]

Notice the last-stock variable floating around in there. Although this is not a good programming practice, it is allowed. In V2, last-stock would automatically become a global variable. But what happens in V3 modules?

In V3, such variables are best referred to as free variables of the module. You can think of them as "local globals" for the module. The last-stock variable above would be accessible within the mod-stock module, but not outside it.

In addition, if last-stock was exported from any default modules that are imported by mod-stocks, then it would refer to those exported values. After all, that is the same reason why func, if, not, and all other standard REBOL values are available here. They are imported from other modules.

A bit deeper...

To understand a little deeper why this behavior occurs, you need to know more about how bind works for modules. Keep in mind that modules are independent contexts. They are independent namespaces.

In REBOL V3, when you load a module in this fashion, all of its words are collected into a new and separate namespace. The words are no longer bound to the global context of your main program. They are independent.

But it is very important to note that this only happens when you load a module directly as a file. If you are creating it from other code, then you must heed the warning below.

Beware of prior bindings...

REBOL V3 allows you to create modules not just by loading source files, but also dynamically. The above example module can be created dynamically with code such as:

mod: make module! [...]

Note that when you do this, the words of the module's block are already bound to the context of your current program.

So, if you dynamically create a module, some of its words are already be bound to your current context. Here, last-stock is an example of such a binding:

last-stock: none

mod: make module! [
    [
        title: "Stock Handler"
        module: 'mod-stocks
    ]

    stocks: []

    buy: func [symbol price] [
        last-stock: symbol
        repend stocks [symbol price]
    ]

    ...
]

This is the result of the fact that the module block was bound when bind was invoked for the main part of the program.

You may be asking yourself, "why would I ever want to do this?" Well, what if you want to define in your main program some of the functions (or overwrite some of the standard system functions) that are being used in the dynamically created module?

So, that is an interesting result, and we will want to revisit this later, because it has deeper issues. But, I don't want to go too deep in this acticle.

Note that if you really want the full "unbound" affect, you could unbind the module block with a line such as:

mod: make module! unbind [...]

It works as you would expect, but note that all of the module's free variables are still present in your current context, regardless of the fact that they are not being used.

19 Comments

REBOL 3.0
Updated 22-Nov-2024 - Edit - Copyright REBOL Technologies - REBOL.net