Console Treatment of VOID vs. TRASH

Rebol2 and Red both have a console property that when the console sees an UNSET!, it prints nothing:

>> block: reduce [<a> #[unset!] <b>]
== [<a> unset <b>]  ; bad rendering, conflates #[unset!] with the word `unset`

>> first block
== <a>

>> second block

>> third block
== <b>

This doesn't provide the best grounding in the console, especially considering that in their world an UNSET! is a reified value that can be found in a block.

However, returning an UNSET! is how functions like PRINT avoid outputting anything with == in the console:

rebol2>> print "Notice no == result"
Notice no == result

rebol2>> type? print "Test"
Test
== unset!

But What Result Should Ren-C Suppress?

Ren-C has two antiforms which might be considered candidates for not displaying... VOID and TRASH.

Because voids vanish (when APPENDing, or COMPOSE-ing), it might seem to make the most sense to have voids not print anything, and trashes print out the standard isotopic form.

Such a world would look like this:

>> void

>> lift void
== ~[]~

>> ~
== \~\  ; antiform (trash!) "tripwire"

If you buy into that, it might seem to make a lot of sense to have functions like PRINT and HELP return VOID.

BUT as I explain in "Why doesn't PRINT return VOID or GHOST", there is a bit of a pitfall. Voids are friendly in terms of opting out of things:

>> append [a b c] print "If PRINT returned void..."
If PRINT returned void...
== [a b c]

This seems too friendly to me. There's another possibility of returning GHOST, which today prohibits use as an argument. It would wind up making an evaluation appear to be void if no other expressions were in play...but if other expressions were involved it would let them fall out

>> print "If print returned GHOST!"
If print returned GHOST!

>> append [a b c] "If print returned GHOST!"
If print returned GHOST!
** Error: APPEND is missing its VALUE argument

>> 1 + 2 print "If print returned GHOST!"
If print returned GHOST!
== 3

So returning TRASH feels like it makes the most mechanical sense...it has the right amount of ornery-ness:

>> print "Mechanically this works best"
Mechanically this works best
== \~\  ; antiform (trash!) "tripwire"

But it's ugly to have that == \~\ ; antiform ... after every HELP or PRINT or other function.

Historically I've gone with TRASH being invisible, and VOID printing a result.

>> ~

>> void
== \~[]~\  ; antiform (pack!) "void"

But I've given a try at printing the results always to see what my feelings are.

1 Like

I gave a demonstration of Rebol to the KYOSS Group yesterday, and had to sort of wince at that basic first-impression situation, even with fairly "light" output:

>> print "Hello"
Hello
== \~\  ; antiform

It opens a can of worms right off the bat, by throwing the "antiform" at people.

Though outputting nothing at all when you print is far from a universal expectation. Firefox and Chrome's JavaScript's consoles are explicit about returning undefined from functions like console.log:

 > console.log("hello")
   hello                       [VM146:1]
<- undefined

Clojure prints out nil:

=> (print "Hello World")
 Hello World
 nil

So utter silence doesn't seem like it's an expectation when you type PRINT. It's just that something about the ; antiform part that makes it unsettling to a first-time user. A simple ~\ might seem okay by comparison:

>> print "Hello"
Hello
== \~\

This only works if voids are picked as what the console does not display.

I can't avoid the fact that vaporization of PRINT makes me uneasy. I prefer making you write elide print.

There are downsides to it, but there are also upsides. It means PRINT would go back to being unusable in assignments:

>> x: print "Hi"
** Script Error: Can't assign GHOST! to variables

But you'd be able to tack it on the tail of expressions:

>> x: (1 + 2 print "Hi")
Hi
== 3

Maybe more people consider that a feature than bug... @rgchris and @gchiu have repeatedly asked why can't PRINT vaporize instead of having to write elide print. I've been stubborn about resisting, but mostly because I don't want people to become too casual about making vaporizing functions...the fewer, the better.

It's an immovable-object-meets-unstoppable-force situation...where I don't want to compromise the accuracy of the console, and I also don't want first impressions of the console to slap people in the face with confusion.

This behavior has flipped around more times than I can count, but I think when all is taken into consideration the right thing to do is to not display TRASH.

It's a curious case of going full-circle, to align with historical Rebol's choice that what doesn't show is the same thing as what's in an unset variable. (Though there really are only so many choices.)

This does mean you don't see a "fully instructive feedback" on when something generates nothing. ...BUT, some cases like unset variables will often error before you even see that:

>> foo: ~

>> foo
** Error: ...

So that's at least some argument further in favor of it.

Remember, There Are Other UI Options in Rich Consoles

I think that if a light gray == \~\ ; antiform showed up and then faded out of the display, that might make a neat default. I'd have to see the effect.

1 Like