Reconsidering Return Values From IMPORT vs. DO

The state of things right now is:

  • DO returns the evaluative result of running a script, which can be any datatype. It can return an INTEGER!, an ACTION!, an OBJECT!, or nothing at all (e.g. a ~none~ or ~unset~ isotope). If it wanted, it could also return a MODULE!...but this module could be unrelated to the context the script you are DO-ing is running in.

  • IMPORT will return the MODULE! of the script, if you use an assignment form on its left.

    module: import %some-library.r
    

    This module is a way of accessing any of the EXPORTed things from the module, as module.exportedthing ... but you can't access definitions inside the module that are not exported in this way. (Or at least you shouldn't be able to.)

    If you don't have a SET-WORD! on the left, it assumes you want to make all the exports of that module available in your current...uh, let's say "binding environment"...

    import %some-library.r
    

I'm deliberately vague about what this is actually doing, because historical Rebol doesn't have answers to binding that demonstrate reasonable composability...and I'm dabbling in trying to make solutions that will work for bigger projects.

IMPORT's SET-WORD! Lookback Is Probably A Bad Idea

What if you wanted:

func-that-takes-module (import %some-library.r)

You don't want to have to write:

func-that-takes-module (dummy: import %some-library.r)

Or:

func-that-takes-module ([@]: import %some-library.r)

The thing I've been leaning toward is a generalized operator which lets you pull an object's fields into the ensuing scope. A prototype exists right now, it's called USING:

obj: make object! [x: 10 y: 20]
using obj
print [x + y]  ; would print 30

So if you didn't want to get a module as a value but rather "use" that, you could say something more like:

using import %some-library.r

And then we could offer a shorthand when USING received a file, like:

using %some-library.r

In fact, I think this could be popular enough to warrant overtaking the name USE.

use parse [10 "twenty"] [
    emit x: integer!
    emit y: text!
] else [
    fail "Expected integer followed by text"
]

print ["Integer was" x "and text was" mold y]

Though from an overloading standpoint, it's a bit shaky when you leverage the same name for both running already loaded code in your environment as pulling something off a network. (Today's DO has this problem, that someone can wedge in a little bit of code to trick you into running arbitrary code off a network instead of a local BLOCK!) So maybe having the FILE!-and-HTTP capable USING be distinct from USE would be a smart distinction.

That would mean that:

using %some-library.r  <=>  use import %some-library.r

How To Get More Granularity?

I've pointed out the variations of import syntaxes in JavaScript before when talking about a design for IMPORT:

import defaultExport from "module-name";
import * as name from "module-name";
import { export1 } from "module-name";
import { export1 as alias1 } from "module-name";
import { export1 , export2 } from "module-name";
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export1 [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
var promise = import("module-name");

Their import defaultExport from "module-name"; case acts like what I'm proposing for using %module-name.r above.

In my other post I bring up these issues. It seems like USE would want this too. But as my other post argues, the dialecting is difficult to make look good.

Extra Return Results

The core routine that is behind both IMPORT and DO actually has two return results... it gives back both the context the code was run in and the final by-product of evaluating the script. IMPORT only returns the module, and DO only returns the product.

It doesn't make a lot of sense to ask IMPORT for the evaluation product, because import caches the module. So it may not run the code in response to the IMPORT, and wouldn't have the result available (unless it saved it somewhere?) Right now you can ask it, but it might just give you back the BAD-WORD! of ~cached~ to tell you that no script code ran. This indicates to me that you probably shouldn't be able to ask.

Perhaps there are more instances where asking DO for the module would make sense. I don't know--and there is some friction with the question of this separation. Should all DO be guaranteed to not contaminate the environment they are called from...forcing you to use an IMPORT if you want the contamination? (This would mean Redbol's DO would have to be rigged up to actually be an IMPORT underneath.)