3

I'd like to know the behaviour of R3 when processing the Needs field of a script header and what implications for word binding it has.

Background. I'm currently trying to port some R2 scripts to R3 in order to learn R3. In R2 the Needs field of a script header was essentially just documentation, though I made use of it with a custom function to reference scripts that are required to make my script run.

R3 appears to call the Needs referenced scripts itself, but the binding seems different to DOing the other scripts.

For example when %test-parent.r is:

REBOL [
    title: {test parent}
    needs: [%test-child.r]
]

parent: now
?? parent
?? child

and %test-child is:

REBOL [
    title: {test child}
]

child: now
?? child

R3 Alpha (Saphiron build 22-Feb-2013/11:09:25) returns:

>> do %test-parent.r
Script: "test parent" Version: none Date: none
child: 9-May-2013/22:51:52+10:00
parent: 9-May-2013/22:51:52+10:00
** Script error: child has no value
** Where: get ajoin case ?? catch either either -apply- do
** Near: get :name

I don't understand why test-parent cannot access Child set by %test-child.r

If I remove the Needs field from test-parent.r header and instead insert a line to just DO %test-child.r then there is no error and the script performs as expected.

earl
  • 40,327
  • 6
  • 58
  • 59
Brett
  • 428
  • 3
  • 11

1 Answers1

2

Ah, you've run into Rebol 3's policy to "do what you say, it can't read your mind". R3's Needs header is part of its module system, so anything you load with Needs is actually imported as a module, even if it isn't declared as such.

Loading scripts with Needs is a quick way to get them treated as modules even in the original author didn't declare them as such. Modules get their own contexts where their words are defined. Loading a script as a module is a great way to use a script that isn't that tidy, that leaks words into the shared script context. Like your %test-child.r script, it leaks the word child into the script context, what if you didn't want that to happen? Load it with Needs or import and that will clean that right up.

If you want a script treated as a script, use do to run it. Regular scripts use a (mostly) shared context, so when you do a script it has effect on the same context as the script you called it from. That is why the child: now statement affected child in the parent script. Sometimes that's what you want to do, which is why we worked so hard to make scripts work that way in R3.

If you are going to use Needs or import to load your own scripts, you might as well make them modules and export what you want, like this:

REBOL [
    type: module
    title: {test child}
    exports: [child]
]

child: now
?? child

As before, you don't even have to include the type: module if you are going to be using Needs or import anyway, but it would help just in case you run your module with do. R3 assumes that if you declare your module to be a module, that you wrote it to be a module and depend on it working that way even if it's called with do. At the least, declaring a type header is a stronger statement than not declaring a type header at all, so it takes precedence in the conflicting "do what you say" situation.

Look here for more details about how the module system works: How are words bound within a Rebol module?

Community
  • 1
  • 1
BrianH
  • 2,186
  • 16
  • 14
  • Thanks Brian! Something to chew on while I try to get my scripts playing nicely both on R2 and R3. – Brett May 10 '13 at 00:21
  • Well, that's simpler than you think: R2 doesn't let you use the Needs header for anything other than version and component requirements. So the only way to make your code work in R2 and R3 is to use `do` and give up on modules, for now. Relaxing the Needs restriction for R2 was on the todo list for the next version, if that ever comes (there is no new R2 version planned) - we were hoping to make a backport of a subset of R3's module system to R2. – BrianH May 10 '13 at 00:31
  • Hi Brian, great information, as always. One more question regarding the loading of a script as a module: a script loaded as a module can then only be loaded for it's side effects, because nothing can be imported into the current context? Would it make sense to add another refinement to import to define which words should be imported from the script? – ingo Jul 24 '13 at 18:36
  • Well, with no name and no exports header, a script would have to be treated that way, as an unnamed private module. Loading a script that way is usually done to cut down on unintentional side effects in a script that you want to load for its intentional effects. As for the refinement you're requesting, that's selective import. I'm working on that already, we hammered out some syntax and semantics for it at the conference. Probably won't be a refinement though. – BrianH Jul 27 '13 at 19:11