So this instinct about "leaky ghosts" (e.g. functions that sometimes return ghost, and sometimes do not) has turned out to be ¡mucho importante! ![]()
I wrote about it in PARSE here:
https://rebol.metaeducation.com/t/leaky-ghosts-what-combinators-should-vanish/2437
But it's a pretty big deal in EVAL. You don't want the EVAL in situations like this to vanish:
^result: switch ... [
...
... [counter: counter + 1, eval code]
...
]
If EVAL is allowed to vanish, then you could wind up assigning the calculation (counter + 1) to result, and this is surprising.
But you need some way of evaluating and being able to get voids if you really intend it, and are conscious of what you're doing.
That's Now Done With The ^ Operator
foo: func [return: [void! integer!] x [integer!]] [
if x > 1000 [return ()]
return x
]
>> "some stuff" foo 304
== 304
>> "some stuff" foo 1020
== \~()~\ ; antiform (pack!) "heavy void"
>> "some stuff" ^ foo 1020
== "some stuff"
So ^ is actually modifying the function call machinery, saying "if this function isn't ghostable, don't do the normal switcheroo from ghost to empty pack".
(I know this isn't necessarily super obvious. But what @ does is a quirk of the system as well, applied to solve tricky fundamental problems that can't be done other ways.)
This Is No Small Issue, And Worth It To Solve
It comes up a lot of other places, e.g. UNLIFT... which takes in a quoted or quasiform and drops it a level. Should that vanish by default, or do you need yet another UNLIFT:GHOSTABLE?
There's an issue with ^var vanishing and causing surprises as well.
This Allows VOID! Branches
>> "some stuff" if 10 < 20 [elide print "Oh my."] else [<else>]
Oh my.
== \~[]~\ ; antiform (pack!) "heavy void"
>> "some stuff" if 10 > 20 [elide print "Oh my."] else [<else>]
== <else>
>> "some stuff" ^ if 10 < 20 [elide print "Oh my."] else [<else>]
Oh my.
== "some stuff"
>> "some stuff" ^ if 10 > 20 [elide print "Oh my."] else [<else>]
== <else>
And it means that eval [] actually returns VOID!... as it should. But EVAL would not be defined as "VANISHABLE" so you'd get a heavy void unless you used ^.
Applied To The Original ANY and ALL Question
>> all [when okay [<a>] while [null] [<b>] when okay [<c>]]
** Script Error: empty PACK! is not truthy or falsey
>> all [when okay [<a>] ^ while [null] [<b>] when okay [<c>]]
== \~\ ; antiform (void!)
The GHOSTLY operator is still useful, but it wouldn't come up quite as often... you'd need to have the "heavy-voidification" safety feature kick in from aggregate expressions to where it was too late for the ^ on the outer construct to help:
>> all [(print "for instance" opt null)]
for instance
** Script Error: empty PACK! is not truthy or falsey
>> all [ghostly (print "for instance" opt null)]
for instance
== \~\ ; antiform (void!)
Note also that OPT could be used to canonize HEAVY VOID back into VOID!s? Though that might accidentally erase NULL if you wanted null to be an error...
>> all [opt (print "for instance" opt null)]
for instance
== \~\ ; antiform (void!)