Considering an issue from:
https://rebol.metaeducation.com/t/how-to-choose-between-returning-none-and-void/2171
There's some touchy-feely choices made in the constructs regarding VOID! (comma antiform) and HEAVY VOID (empty pack!).
I think it's important that neither of these "voidlike" values be truthy nor falsey.
>> if ^void [print "This is important."]
** Script Error: IF is missing its CONDITION argument
>> if (pack []) [print "This too."]
** Script Error: IF is missing its CONDITION argument
However, ALL is kind of like a chain of IF statements testing for logic. So given the existence of VOID!, should it demand VOID! for opting out... and error on empty packs?
>> 1 + 2 while [null] [<b>]
== \~()~\ ; antiform (pack!) "heavy void"
>> all [if okay [<a>] while [null] [<b>] if okay [<c>]]
** Script Error: empty pack is not truthy or falsey
If it wasn't willing to erase empty packs, you'd need to use GHOSTLY or IDENTITY to erase the empty packs:
>> all [if okay [<a>] ghostly while [null] [<b>] if okay [<c>]]
== <c>
>> all [if okay [<a>] ^ while [null] [<b>] if okay [<c>]]
== <c>
It's probably the case that needing to intervene to stop the error is good. That's consistent with the protections of the evaluator in other instances to consider the heavy-voidification as a safety feature, to stop you from mistakenly thinking your value is coming from the last slot when it's not obvious at source level that it isn't.
Note that other constructs--like CASE--do not erase empty packs...only vanishable VOID!. They have a "structure" to them, and it's desirable to not disrupt that structure too easily.
I think it should be a very rare choice to make things return an empty pack unconditionally. And I think vanishable functions should be rare.
Vanishability is nice for debugging statements, because you can throw them in without disrupting the surrounding code
>> y: 10 + eval [x: 1 + 2, dump x]
x: 3
== 13
Because of this, there's been some pressure to say that PRINT be vanishable. So far, I've rejected that.
append [a b c] print "I think this should error" ; rules out vanishable
Also, though people reach for PRINT for debug output, I think it's poor for that. The fact that it evaluates blocks means I don't like the idea of print x taking too many kinds of input, that could one day become a block and surprise you by evaluating. So it's already a bad generic "debug dump". And logging with the same thing you use for committed output makes it hard to search for debug code and remove or disable it.
I like there being relatively few vanishable operations, and if you want to erase something use ELIDE.
But ASSERT is vanishable, and that's neat in things like CASE or SWITCH for asserting something as true when you've gotten to a certain point:
case:all [ ; ALL -> don't stop on first condition matched
x < 10 [y: <lesser>]
x > 10 [y: <greater>]
assert [(x = 10) or (find [<lesser> <greater>] y)]
x = 10 [print "You can imagine this kind of thing being useful"]
]
I should go through and replace these with real useful examples someday, but my hope is people get the point abstractly.
Anyway, I think it's nice to be able to do that particular thing without saying ELIDE ASSERT. A few other constructs make the cut of justifying vanishability.
Hopefully it's clear why I don't think there are that many applications for functions that return heavy void conditionally. GHOSTLY is a niche function that I don't think there are that many legitimate uses for.
And if you're going to come up with a construct that is conditionally vanishable, it should be discernible from the source-level syntax if it's a vanishing or non-vanishing invocation.