So I know that I was the one who propagated the idea of _ being a representation of "nothing here", with the BLANK! datatype... an invention from the early days of Ren-C.
I wanted to reserve it, so it couldn't be reassigned. That's still true today: underscore can reliably be used in dialects without concern that it has been given a value:
>> for-each _ [a b c] [probe _] ; signal for "no iteration variable"
_ ; not "a"
_ ; not "b"
_ ; not "c"
It's thus unreassignable, and it's used other places for this purpose. Here's that emphasized with a little multi-return demo of the niceness of Ren-C find:
>> pos: find "abcdef" "cd"
>> pos
== "cdef"
>> [start end]: find "abcdef" "cd"
>> start
== "cdef"
>> end
== "ef"
>> [_ end]: find "hijklm" "jk"
>> _
== _
>> end
== "lm"
So underscore still is serving its roles, as it did when it was BLANK!.
HOWEVER...
_ is No Longer BLANK!, It's A Lexical SPACE Character
Underscore is now a "RUNE!" (the unified class of ISSUE! and CHAR!), and it represents a space.
>> form _
== " "
>> rune? _
== \~okay~\ ; antiform
See "Reified Unreassignable Nothinginess: SPACE RUNE!s"
Anything that has only underscores in its representation is still a RUNE!, just with more spaces in it:
>> tab: ____
>> rune? tab
== \~okay~\ ; antiform
>> form tab
== " "
So you can persist and sense how many underscores are there.
But if a token is not all underscores, then underscore is a word character.
for-each 'w [_foo_ _bar baz_ foo_bar _bar_baz] [assert [word? w]]
I've had no regrets about this choice. And since BLANK! had stopped being falsey a while beforehand, the transition to truthy space was no problem.
(A singular falsey state of null--that is an antiform and can't appear in blocks--is also something I have no regrets about. I'm eager for people to see all the grand benefits this has brought.)
My Regret Is My Original Choice Influenced @rgchris Strongly
Here's what he said in the README.md of Ren-C scripts in GitHub rgchris/Scripts:
[I found it beneficial that Ren-C added] "... the literal
_for NONE! values. This has proven effective on syntactic, semantic, and cognitive levels providing a subtle and intuitive solution to a longstanding omission in Rebol grammar. Surveying code and data—technically the same thing in Rebol—is greatly enhanced by this change and should be a part of the Rebol lexical canon.
He convinced Oldes to make it a notation for NONE!, so this is giving rise to underscores everywhere:
copy #[
type document
name _
public _
system _
form _
head _
body _
parent _
first _
last _
warnings _
]
intersect node either 'document = node/type [
#[type _ name _ public _ system _]
][
#[type _ name _ value _]
]
Only One Single-Character Alternative
The only single-character alternative I have to offer at the moment would be antiform space, the ~
intersect node either 'document = node/type [
#[type ~ name ~ public ~ system ~]
][
#[type ~ name ~ value ~]
]
But under evaluation, that produces something that is not falsey... antiform runes are trash.
I will say that in Ren-C programming, I do encourage thinking about choosing between null and trash. These are distinct intentions:
obj: make object! [
field1: ~
field2: null ; or ~null~ if you want a quasiform vs. variable lookup
]
Both are considered empty for the purposes of DEFAULT. But accessing field1 will error, while field2 will not error on fetch and be falsey.
One should choose wisely...and also note that since trash is an antiform RUNE!, it can hold a string, which turns out to be very useful. See Labeled Trash RUNE!s in the Wild for examples.
Immovable Objects, Unstoppable Forces
Underscore cannot represent null intent directly: it's unreassignable, by design.
The only answer would be some kind of object/map-making dialect. Especially since the emerging pattern is to not use SET-WORD!s either.
Let's say this dialect is actually what {...} does (or maybe {{...}}?). RUNE! literals aren't allowed, let's say you put them in groups, and groups evaluate:
intersect node either 'document = node/type [
{type _ name _ public (#a) system _}
][
{type _ name _ value _}
]
Anyway, porting modern @rgchris code is likely best going this route, vs. disrupting it more.
Hopefully we can agree on a good dialect here. But even if we can't ultimately agree, remember that with RebindableSyntax, you can customize what {...} does (per user, per module, per function...)
UPDATE: Moved CONSTRUCT dialect for
{...}discussion here:https://rebol.metaeducation.com/t/construct-dialect-for-objects-maps/2569
...so this thread can remain on topic specifically about underscores and R3C.