REBOL 3.0

Introducing the Concept of a Current Module

Carl Sassenrath, CTO
REBOL Technologies
24-May-2006 23:53 GMT

Article #0029
Main page || Index || Prior Article [0028] || Next Article [0030] || 18 Comments || Send feedback

Let me just say this outright: my Modules document is not very good. Sorry, but it's just not very clear. It is quite old, and as soon as I get a better doc written, I promise I will delete that old one.

So, to make it clear: Modules provide a way to extend the REBOL environment. That's their purpose.

Of course, that statement is not indended to mean that everything about modules is simple. It is not. But, to understand and to use modules must be simple. Anyone and everyone should be able to do it. (Otherwise, I would not be doing it, right? You know me.)

So, this means a few things:

1. You need to be able to create a module just as simply as loading a script that looks like:

REBOL [title: "My module" type: module]
...

and alternately you can use this method at any time in your code:

my-mod: make module! [
    [title: "My module"]
    a: 10
    func1: func [...] [...]
]

Note: that means you can create multiple modules within a single script, if that's what you need to do.

2. You should be able to access the exported values of a module easily. How easily? Well... automatically.

For instance, if your module is:

REBOL [title: "My Math" type: module export: [buy sell]]
...
buy: func [value price] [...]
sell: func [value price] [...]

then later, you run a script that does this:

buy "google" $200
sell "microsoft" $10

It should work as you would expect. It must be just that easy.

3. There are many extra options that are the other 20%. Yes, they are useful, but most of the time you don't care.

Now, that's where it gets a bit more complicated. Let me give you a good example:

Let's say a section of code runs an external script using the do function. To what module do the words of that script get bound? This is a non-trivial question. I mention it go get you thinking. Deeply, I hope.

Why does it matter? Here's why: if you are using a function of a REBOL module (call it Mod-A) that looks like:

do-it: func [file] [
    ...
    do file
    ...
]

are the variables of the newly evaluated file bound to Mod-A or are they bound to the module that called do-it in the first place?

Over the years, I've found that when these questions come up, there is some kind of implicit action taking place. There is an assumption going on. In this case it is what module do the new words get bound to? We have two choices:

  1. The module where the do was evaluated.

  2. The original module where the do-it was evaluated.

In low-power static languages like C and most others, this would be an easy choice. But, in the dynamic evaluation of REBOL, it's not quite as easy to decide. Both choices are valid.

Like in other areas or REBOL, the better design choice must come from some kind of common sense rule. Let it be this:

  • It is best to avoid the accidental auto-expansion of modules.

Such results would be problematic because arbitrary new functions could be added to any of the existing standard modules of the system. That wouldn't be a good thing.

Therefore, according to this rule, the do above would bind new words to the current active module. So there is the implicit action: the concept of a current module.

When your user script gets evaluated, REBOL will launch it within its own unique "user module". And, when any additional scripts are loaded and evaluated, by default they are bound to the same user module unless they are explicitly directed elsewhere.

Now, what if you want a section of code to refer to an explicit module? What if you do a load or a do and you want a specific module to be affected? That's the purpose of the with function:

Just as you can say:

with object [... expressions ...]

Here object is any valid object, and the block is evaluated within the context of an object, you can also say:

with module [... expressions ...]

Where module is any existing module. This line specifies that the expressions are bound to the context of a given module.

You can even write:

with module [do %script.r]

to force the module to accommodate the new script's bindings.

In addition, to help make this action easier, you can also use any word with a context to provide the context itself. That way you don't have to figure out how to obtain the reference to the context. For example:

with 'word [do %script.r]

where word is bound within the target context.

I think those of you who are expert REBOLers see that with is more than just another name for bind. A hidden variable is being set that indicates the module, the "current environment" for the block. And, there are some deeper implications that go along with it (such as what happens when an error occurs that throws us out of the with block.)

So, there you are. Simplicity with just a pinch of spice to create the very best of flavor. Now, if only I could cook everything that well.

Please post your comments. I am open to your ideas. Thanks.

18 Comments

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