Design for IMPORT

Getting to be around the time where I have to do something about this. (I need the form of import that gives back a module without pulling it into scope...)


I can't shake the feeling that when you have a list of configuration things that is possibly infinite, you don't want that potentially infinite list as the first argument.

Looking at this flipped (and switching back to historical .r)

use import %ws-runtime.r <*> 
use import %ws-runtime.r [foo bar] 
vm: import %ws-runtime.r

Now we can ask: how would USE interpret a filename if not as a module? Could the import be implied?

use %ws-runtime.r <*> 
use %ws-runtime.r [foo bar] 
vm: import %ws-runtime.r

Bringing me back to: does the arity-2 nature of USE convey the grouping well enough? If not, we're back to a single arity default:

use %ws-runtime.r 
use [%ws-runtime.r foo bar]
vm: import %ws-runtime.r

Sidenote here, what (if anything) is the result of a USE operation? It's importing symbols into a context, so maybe the aggregate context? Seems to make the most sense, and it's something it has on hand.

let a: 10
let b: 20
aggregate: use make object! [c: 30 d: 4] 
let e: 30

>> aggregate.a
== 10

>> aggregate.c
== 30

>> aggregate.e
** Error e is not in there

Seems vaguely sane. So in this world use %foo.r would be a synonym of use import %foo.r

It's possible--perhaps--that USE could be an arity-1 equivalent of BIND.

So for example, you might want to import a module's definitions so just one function uses it. You don't want to do that every time you run the function, so:

something: func [...spec...] (use %some-module.r [...body...])
something-else: ... ; no %some-module.r influence, GROUP! ended

The use is injecting that module's influence in the evaluation stream, so when the body block evaluates it gets that binding wave added on. But perhaps you could also write that as:

something: func [...spec...] bind %some-module.r [...body...]
something-else: ... ; no %some-module.r influence, BIND arity-2

So you wouldn't need to throw up a GROUP! or something to stop the USE propagation from spreading. It seems logical, that anything you might want to do one way you might want to do the other way.

Sounds nice on paper. :page_with_curl: But there's a big wide open design space there about how to subset a binding operation to just the things you want.

use [
    %some-module.r [x y z]
    %some-other-module.r [a b ccc: c]  ; alias c as ccc?
]

And maybe not all the options have to go on all the things. The above could cover the common IMPORT case, but perhaps you could break it apart:

some-mod: import [%some-module.r <lots> /of #crazy op.t:i/o:n.s]

use [
    (some-mod) [x y z]
    %some-other-module.r [a b ccc: c]
]

It's starting to look palatable... especially if USE and BIND can be unified.

What About Old Rebol2 USE?

What old Rebol2 USE used to do, was effectively EVAL BIND:

x: "outside"
use [x] [  ; old meaning of USE
     x: "inside"
     print [x]  ; prints inside
]
print [x]  ; prints outside

This form of USE tended to be used to do some prep work and return blocks, e.g. a BIND operation that wanted to do some additional work:

make-thing: func [...] [
    return use [x] [
        x: 1 + 2  ; concept is there's setup code, so you can't just bind
        [some block referring to x]
    ]
]

Things are a lot different today, and you can accomplish this with LET and other constructs.

    return eval bind [x] [
        x: 1 + 2
        [some block referring to x]
    ]

    return (
        let x: 1 + 2
        [some block referring to x]
    )

    return (
        use {x: 1 + 2}  ; FENCE! behavior not decided, but probably this works
        [some block referring to x]
    )

I think the idea of being able to slipstream bindings into the current evaluation stream is a higher calling for USE, and I don't think this arity-2-evaluating form is a big priority.