Rebol2 and Red let you DO a string, and it picks up variables from the currently running context:
rebol2>> x: 10
== 10
rebol2>> y: 20
== 20
rebol2>> do "print {running} x + y"
running
== 30
Ren-C won't DO any string unless it is a fully formed script (with a Rebol header).
>> do --[print "running" 1 + 2]--
** PANIC: IMPORT and DO require a header on: <source>
>> do --[Rebol [] print "running" 10 + 20]--
running
But it discards the result (no == 30)
Ren-C DO won't run blocks either (you use EVAL for that). But EVAL doesn't like strings at all:
>> eval "1 + 2"
** PANIC: eval expects for its `source` argument: [
<opt-out> "useful for `evaluate opt ...` scenarios"
block! "always 'afraid of voids' semantics, :STEP ok"
group! "must eval to end, only afraid of ghosts after value seen"
frame! "invoke the frame (no arguments, see RUN)"
warning! "abruptly report error (prefer PANIC)"
varargs! "simulates as if BLOCK! is being executed"
]
Also, DO can't access variables from the current context:
>> x: 10
== 10
>> y: 20
== 20
>> do --[Rebol [] print "running" x + y]--
running
** PANIC: Couldn't get binding for x
Bunch Of Questions
Why won't DO run strings without a header?
Why does EVAL take BLOCK! with no header but won't take strings?
How do you get a result back from a DO of a Script?
How do you make variables from the currently running script visible to a script you call with DO?
Red/Rebol2 Binding In String DO Was A Flimsy Illusion
It had no context-awareness. It only appeared to work because there was no modularization, so everything could just see the same globals. If you tried that expression inside a function, the locals would not be seen:
rebol2>> unset 'x
rebol2>> unset 'y
rebol2>> add-xy: func [x y] [do "x + y"]
rebol2>> add-xy 10 20
** Script Error: x has no value
Ren-C Has Powerful Binding, You Can Use It
You can TRANSCODE your string, and when you do, it will not be bound for starters:
>> transcode "x + y"
== [x + y] ; unbound
>> eval transcode "x + y"
** PANIC: Couldn't get binding for x
** Id: no-binding
But unbound material is a useful currency in Ren-C, only needing binding at the tip to imbue it with life.
There are lots of ways to take control of binding at a granular level. But the $ operator is the simple and common way of applying the binding from the current context to just the tip of an item:
For people who dislike symbols, it's theorized that the BIND dialect (similar in spirit to @hiiamboris's WITH) will have a way to say this. Perhaps it will look something like:
The DO machinery is high-level: it's for scripts that are sandboxed into their own world. You aren't splicing code into your current execution environment.
Which leads us to what you can do:
...because if you want to splice code into your environment--only you know how weird and unsafe you want to be about it. You have TRANSCODE, turn your string into a block... you have BIND and $ and LET.
There's negative value in EVAL guessing what you mean by a string, because unlike a BLOCK! a string can't carry bindings with it. We just don't guess.
eval $ transcode is as brief as I think you need for what someone might imagine as a "default" behavior for EVAL of a string. I prefer to throw in that transcode and bind step just to be explicit...otherwise EVAL would have to be able to "sense" the current context.
(Sometimes a function has to implicitly receive the current context, like COMPOSE does to know variable names to interpolate into strings. But we want to minimize the number of functions that do that. I don't think there's a good argument for making the EVAL that comes in the box do that, but you could make your own EVAL-like function that did. And you could call it EVAL if you really wanted to, I guess. )
Here's something that might surprise you about DO in Redbol:
rebol2>> do "Rebol [Title: {My Script}]"
== [title: "My Script"]
red>> do "Red [needs: view]"
== [needs: view]
If you DO a textual string in Rebol2, Red, or R3-Alpha... the header isn't taken into account. The only reason it doesn't have an error is that Rebol is an object and Red is a tuple.
rebol2>> do "type? Rebol"
== object!
red>> do "Red"
== 255.0.0
So they're inert, and so is the BLOCK! after it.
Basically, the header is only taken into account if you use DO on a FILE!. I don't like this invariant, e.g. I think that these should be the same:
do %script.r
do read %script.r
So long as all words are fair game, you have to know from context whether a header is expected or if it's just code that happens to start with the word rebol or red. I don't like the idea of needing a refinement or option to tell DO whether you're expecting a header, and instead just say you always are.
QUIT in Ren-C is definitional to the script (or module) that is being initialized. It can only QUIT that script (or finish the initialization of that module). Once the script has finished, that QUIT is no good any more.
So you can use that QUIT a bit like a definitional RETURN from a function to give the script's return value. It takes an exit code integer by default, but if you want to quit with a Rebol value (e.g. an INTEGER!-as-integer, not an exit code) use QUIT:VALUE