I've really found that I like BLANK! literally at source level as a way to say SPACE in string operations.
So it could be useful in PARSE for this purpose:
>> parse "aaa bbb" [some "a" _ some "b"]
== "b"
We haven't talked about the "blank and space" duality for a while, but I'd even gone as far to suggest that when you do something like TO BLOCK! of a string it might transform the spaces into blanks:
>> to block! "the cat"
== [#t #h #e _ #c #a #t]
(People might not recall why I was mentioning this, but around the time of UTF-8 Everywhere it was pointed out that since we had non-fixed-size codepoints, seeking in strings and mutating them could be costly. So if you had a string algorithm you might want to "explode" a string into a BLOCK! representation to work on it. This would give you great flexibility to do things like put in substitutions with full strings, or mark the cells with intermediate states for your algorithm...and then you would collapse it all down at the end by turning it back into a string.)
The Literal Interpretation Is Also Compelling in Arrays/Sequences
I've thought of BLANK! as being the analogue to space in blocks, so matching them literally there makes sense:
>> parse [a a a _ b b b] [some 'a _ some 'b]
== 'b
But where it really shines is in processing things like paths and tuples, to match the gaps in them:
I'm...pretty sure (?) this is still the best plan.
So there's a new philosophy I've outlined for why BLANK! exists at all, and its purposes as being a kind of generic "nothing to see, here" is distinct from what might be thought of more as a disruptor like null or an unset variable. It is not related at all to soft failure. Blank is simply a wildcard that you can choose to treat equivalently to an empty series or missing value, without committing to being anything in particular.
I've also mentioned that in some mechanical contexts (like APPEND), we are simply more interested in blank's "thingness" than in its representation of nothingness. So you have to SPREAD it or otherwise modify it to get it to not act mechanically.
PARSE strikes me as one of the more mechanical contexts.
parse [_ _ _] [repeat 3 _]
And I think the value of having it to represent space in string contexts is quite high.
It may be one of those things where to prevent accidents where you didn't mean it to be interpreted as a space, it shouldn't allow you to use it fetched from a WORD! in the rules. You either use @var to say "I mean literally a blank" or you can make the rule contain a quoted value.
UPDATE 2025: Just Want To To Point Out There Are Other Tools
Things have evolved to where this is the perfect answer.
So if we put this together, then empty splice serves "BLANK"'s purpose...
Like a VOID, it doesn't do anything when you append it to a series:
>> opt null
== ~[]~ ; anti (void, unstable)
>> append [a b c] opt null
== [a b c]
>> spread []
== ~()~ ; anti (empty splice, stable)
>> append [a b c] spread []
== [a b c]
>> append "abc" spread []
== "abc"
NULL would error if you tried that.
But unlike VOID, empty splice is something you can put in a variable:
>> var: void
** PANIC: VOID is unstable empty pack, no value for variable
>> var: spread []
== ~()~ ; anti
>> append [a b c] var
== [a b c]
There's not any particularly good reason why FOR-EACH shouldn't let you enumerate a splice, and if it does, then we could give it the desired behavior of returning void instead of null:
Because it's been back and forth on what _ is supposed to do in PARSE, I hadn't taken the "recognize literally in BLOCK!, act as SPACE" completely for granted in a lot of code.
To play it safe I'd been writing e.g.:
>> parse [#a _ #b] [#a '_ #b]
== #b
>> parse "a b" [#a space #b]
== #b
Now that we know _ is the new representation for the-character-formerly-known-as #" ", the behavior is completely unsurprising... all character literals match literally!
I think that's just the way it goes. Because PARSE BLOCK! rules have to be generic. If you programmatically are generating a rule from items you have in a block and you want to match them, then that programmatic code has to work:
It feels a little bit "crazy" right now because the idea of ' being a quote of what was historically thought of as a "solid" BLANK! entity hasn't sunk in. But with practice, people will have to get used to the new nature (well, not new considering its vanishing in paths and such):
>> ''
== '
>> '
== _
>> quote _
== '
Once you internalize that, the above makes sense.
Quoted matching in strings is a little bit harder to swallow. Before you could do:
>> parse "a_b" ['a '_ 'b] ; when quoted space rendered as '_
== b
The idea was that a quoted thing would match the molded representation of the unquoted thing.
>> parse "[a b] c" ['[a b] 'c]
== c
I don't know if it's as great an idea as it seemed, because '[a b] and '[a b] will have identical molding when matched as a rule. But, I thought it could be "useful enough"
Anyway, if you follow the rule with regularity (and it seems misguided not to), you get:
>> parse "a_b" ['a ' 'b]
== b
Again, maybe that could seem natural to someone who is used to:
>> '
== _
...but I kind of doubt it. If code wasn't being programmatically generated in such a way that "that's just what it made", I'd never use it...and prefer:
>> parse "a_b" ['a #_ 'b]
== b
>> parse "a_b" ['a "_" 'b]
== b
>> parse "a_b" ['a underscore 'b]
== b
...or whatever. I'm not sure, maybe the molding is misguided.