Comments on: Isolated namespaces in modules
Note: The comments section here contains substantial additions and insights on this topic.
R38 will be making some changes to the definition of modules. One change is that a module, when imported, will by default bind with the system exports for its external functions.
Stated another way, modules do not create full namespaces for all words. They will share system words. Reason: most modules work fine sharing the system definitions (especially if we add a locking mechanism to prevent changes.)
The change is simpler than it sounds. Take the module source:
save %mod-test.r [
test: func [a] [print a]
Now, import it normally:
m: import %mod-test.r
probe words-of m
But, if you import with the /isolate refinement, the module's global context will be isolated (but it will still be be resolved with the system export context):
m: import/isolate %mod-test.r
probe words-of m
[test func a print]
So, it works exactly the same. The refinement is only useful for cases where you want to redefine one or more global functions, but only within the context of the module (not for the entire system). So, that's why it is no longer the default.
Looking at the code, it appears that the isolation is only performed the first time the module is loaded. This means that if you want a module to be loaded isolated and can't trust that it has the isolated header you have to do one of three things: |
- IMPORT/only/isolated the module ahead of time, and then manually add the module to the module list.
- IMPORT/isolated the module first in an isolated loader module (will this even work?).
- IMPORT/only/isolated the module by file! or url!, so it will reload even it it is already loaded.
Will there be some equivalent to the IMPORT /isolate option in the needs header? If a module is loaded isolated, are its imported modules also isolated?
All these questions will be answered by the needs code :)
Ok, some answers and notes...
On: "it appears that the isolation is only performed the first time the module is loaded." I'm not sure what you mean by that. In most programs (other than during debug), modules are only loaded once.
But, I should have also noted that the isolate property belongs to the module header itself. The IMPORT/isolate refinement is just a shortcut to force the isolated property, if for some reason you want that set.
One reason to use IMPORT/isolate is during debugging or inspection to view the external references of the module. It's a good indication of how far reaching the module is and also leaky local variables become quite visible.
I'll write more... below. (To avoid one huge comment.)
Isolation is a property of the module itself. It is not related to the state or sequence of importing. So, that answers another question from above. Each import is independent.
I should note for the record that this "non-stateful import" applies to do itself. A module that performs a do is evaluating within the context of and on the behalf of the original program. So, if a module uses do, the bindings that occur are to the original program, not to the module. That comes from the original definition of modules: as being extensions to the REBOL system.
And a final note (for now): Modules imported by name (word!) imply a different semantic than those loaded by filename or URL.
The reason is that the program is using what I would term a generic module reference. In other words, "this program uses the standard GUI" or "the standard HTTP server core".
A filename or URL is a non-generic reference. In fact, the file name can be "aaa" and the module it loads can be 'bbb. The purpose here is to allow programmers to load non-generic modules, ones they built themselves, need to test with, etc. So it lets us build prototype modules that go by filenames like %gui4.03.02.r while we are testing, yet the module itself has the name 'GUI and will prevent the reload of an older GUI system when the test modules simply specify 'GUI.
The change you are blogging about is not the addition of the isolated mode, it is about the addition of the partially-isolated default mode. Modules used to be isolated by default, now they aren't.
A module that performs a do is evaluating within the context of and on the behalf of the original program.
Have we given up on the separated contexts model then? The original reason for system/contexts/current was to allow modules to execute in their own contexts, and that the global context (the "context of the program") was going away. Are we handling sandboxing in some other way? Restricted processes perhaps?
In most programs (other than during debug), modules are only loaded once.
But they might need to export into different current contexts. Non-isolated modules might need to be loaded more than once so that their side effects on the current context can be applied to another current context.
The IMPORT/isolate refinement is just a shortcut to force the isolated property, if for some reason you want that set.
You would want isolate set unless you had examined the source of the module to determine if it was safe to not set.
the isolate property belongs to the module header itself
Which was written by the author of the code you will be running, not you.
If you remember, part of the reason we made modules in the first place was to help enable ReBrowse, which will be assumed to be executing untrusted code, just like a web browser.
On the reason for the blog: exactly. The model has changed. And, I think we agree on most of the details. (However, we need to agree on some standard terms in order to converge. More on that later).
Modules still have separate contexts. That has not changed. The question is how you want to bind externals from within the module.
Take the word PRINT as an example. There are two choices:
- The reference is to an external context (called system/contexts/exports) that defines PRINT.
- The reference is internal, to a word within the module that defines PRINT.
The second is the isolate case, because it allows the module to redefine PRINT without effecting the rest of the system.
However, most of the time, that isn't necessary, so we don't need to make isolate the default case.
The reason it isn't necessary is because secure context sandboxes require a lot more than just this binding change. It must also be possible to protect all shared definitions, or at least make them inaccessible (using deep copy all reflection). Both have advantages and disadvantages, and should be debated.
Anyway, right now the goal is simply this: Get modules working well enough to get some initial modules done... because we immediately get an 80% win over what we've got now. (One big namespace)
For me, the actual driving force is the GUI project (which I believe is critical path). We don't want its internals spilling all over the place much longer (like they do in R2.)
Post a Comment:
You can post a comment here. Keep it on-topic.