The Holy Grail of Hello World and PRINT...Solved?

When I first used Rebol, I thought it was great how clean a PRINT statement could look. Between the curly-brace delimited strings allowing for quotes and apostrophes (and nested braces), there was a pleasing and natural implicit spacing between the parts:

 >> score: 1020
 >> print [{This "score" isn't real, player:} score]
 This "score" isn't real, player: 1020

But when finer-grained control was needed, things started to break down. I won't go into all the problems with REJOIN, but just looking at a simple tweak:

>> user: "Bob"
>> score: 1020
>> print rejoin [
    {This "score" isn't real, } user {:} space score
This "score" isn't real, Bob: 1020

I also don't like spaces on the ends of strings like makes it harder to see the pattern in them and factor them better. And when using double quotes:

["it's hard to " scan for " what's a string " or not]

Without syntax highlighting (which I think is a design goal, here) you look at the middle of such things and don't know what you're looking at.

So this means I always wanted to use BLANK! to mean space, so it could be used more freely and would look nicer. So compare:

rebol2>> print rejoin [
    {This "score" isn't real,} space user {:} space score
This "score" isn't real, Bob: 1020

>> print unspaced [
    {This "score" isn't real,} _ user {:} _ score
This "score" isn't real, Bob: 1020

Note: The frequent need for this level of control even provoked a period of reflection about whether this was clean enough to be the default means of getting spaces in PRINT statements, e.g. that it would be based on UNSPACED instead of SPACED when passed a plain block. However, while the underscores for explicit space look good when you're forced into being explicit, it was still no match for the cleanliness of not having any underscores when implicit spacing would work...which is rather often:

print [{This "score" isn't real, player:} _ score]  ; not bad
print [{This "score" isn't real, player:} score]  ; ...better

Thus the historical behavior was deemed the best default. Though it should be emphasized that UNSPACED and SPACED are based on DELIMIT, which has a number of general improvements over history...and that building PRINT on top of WRITE STDOUT and these constructs offers great flexibility to use the behaviors independently, and there's a lot of cleverness in Ren-C's working for this that wasn't there before.

Are Blanks : Block as Space : String (???)

Beyond thinking this is a clean visual way to see your data, I've wondered if there's a conceptual parity between spaces and blanks.

The counter-argument to that would be that you might imagine blanks to take on a sort of "placeholder" status that actually corresponds to nothing in a string. For instance, a set of operations like this:

>> b: to block! ["abc"]
== [#"a" #"b" #"c"]

>> b/3: "def"
>> b/2: _
>> b
== [#"a" _ "def"]

>> t: to text! b
== "adef"

But of course, if that was your intention you could do it with a REMOVE-EACH step that took out the blanks. Or just remove the spots as you went if you weren't dealing with a situation that was too inefficient.

Historical problem: Use of Blanks as non-noisy NULL

I've already written about the dual responsibility of BLANK!. Consider:

>> description: third ["Cruel" "Brave New"]
; null

>> print ["Hello" description "World"]
** Error: description is unset

You could have said :description, but that doesn't look as good...and using unset variables when you don't need to is kind of a sketchy practice. So instead, the idea was that you'd be able to say the likes of:

>> description: try third ["Cruel" "Brave New"]
== _

>> print ["Hello" description "World"]  ; look ma, no GET-WORD!
Hello World  ; ...and no error!

This seemed like a logical behavior--compared to getting a seemingly "random" behavior of the blank as a space. So early experiments in DELIMIT (the code behind SPACED and UNSPACED) which turned BLANK!s into spaces were backed out.

But...Can a Crazy Idea Save This (and other) Wishes?

I proposed an idea that a plain WORD! or PATH! variable lookup that finds a BLANK! value evaluate to null. This would mean that if description above looked up to a BLANK!, ["Hello" description "World"] would act like ["Hello" null "World"] and not ["Hello" _ "World"].

If you combine this with the idea that PRINT has an /UNSPACED mode controlled by a modal parameter, things get really nice:

>> print @[{This "score" isn't real,} _ user {:} _ score]
This "score" isn't real, Bob: 1020

And the train of thought which led to the idea was to address a completely different issue.

I've managed to get the system to boot with this rule enough to try it. And it looks pretty darn good. The main thing that happened is that a lot of code that used to say:

if find var-that-might-be-block-or-blank item [...]


if find try var-that-might-be-block-or-blank item [...]

Because FIND follows the blank-in-null-out convention, which purposefully errors on nulls, which that blank variable now evaluates to. But bear in mind, the variables aren't called that, so what's actually happening is you're changing:

if find black-box item [...]
if find try black-box item [...]

So having to change the source to this doesn't feel like a bad thing. It feels like now the callsite is telling the reader "oh, hey, blank-box might be blank". You know it's not unset, otherwise the TRY wouldn't help. You'll only be getting a null back in the case that it's blank, and that's the only case the TRY will give you back a blank.

And again, the problem this was originally trying to solve was the question of how routines like APPEND and COMPOSE should handle blanks by default. What this does is pushes the responsibility off of them, and onto the caller to say what they meant by either saying TRY or not.

It's Looking Rather Promising...

One thing I'll say by looking at the code in the system itself is that a lot of times it seems more clear to say SPACE explicitly. But I don't think the code for the interpreter and its extensions are very representative of scripts people tend to write.

Anyone care to throw in thoughts?