You've encountered a safety mechanism... that "busts ghosts"... turning VOID! into HEAVY VOID.
Only A Few Functions Are "VANISHABLE" By Default
There's just too much room for error with vanishing, when you write things like:
x: (<some expression> eval code)
x: (<some expression> ^var)
parse data [... x: [<some expression> rule] ...]
You're using evaluation patterns where you typically expect the last synthesized product to be the overall result of the evaluation. It's disorienting when that product vaporizes too easily.
Surgical Tool: Use the IDENTITY Operator (^)
Asking for the function result "as-is" can be done with the IDENTITY function, which is also available as ^
>> 1 + 2 eval [comment "test"]
== \~()~\ ; antiform (pack!) "heavy void"
>> 1 + 2 identity eval [comment "test"]
== 3
>> 1 + 2 ^ eval [comment "test"]
== 3
>> f: make frame! comment/
>> f.1: "test"
>> 1 + 2 ^ eval f
== 3
>> 1 + 2 ^void
== \~()~\ ; antiform (pack!) "heavy void"
>> 1 + 2 identity ^void
== 3
IDENTITY intervenes and stops the distortion that makes the heavy void result.
But this means that if something is returning heavy void as its actual result (not just a safety intervention from a multi-step evaluation), it won't vanish.
The "Humane" Operator: GHOSTLY
If you don't care about stopping the creation of heavy voids and just want to erase ANY-VOID?--heavy or not--you can use GHOSTLY.
This can be used in places where the influence of IDENTITY would be "too late", such as when a heavy void branch is created:
>> 1 + 2 if 10 = 10 [comment "test"] else [<foo>]
== \~()~\ ; antiform (pack!) "heavy void"
>> 1 + 2 ^ if 10 = 10 [comment "test"] else [<foo>]
== \~()~\ ; antiform (pack!) "heavy void"
It's important to the machinery of ELSE/THEN/ALSO to know if they should run or not. So the actual emerging value out of the failed IF is a light VOID! only if the branch does not run. Otherwise even if the branch runs, it has to be at minimum a heavy void or heavy null to cue the ELSE/THEN/ALSO.
GHOSTLY happily erases the heavy voids that IDENTITY will not:
>> 1 + 2 ghostly if 10 = 10 [comment "test"] else [<foo>]
== 3
IDENTITY Is Available In UPARSE Too
The BLOCK! and GROUP! combinators are "ghostable" by default, but the WORD! combinator is not:
>> rule: [elide some "b"]
>> parse "aaabbbb" [tally "a" ^ rule]
== 3
