Why Isn't PRINT Invisible ("void")?

Since PRINT doesn't have an interesting return value, we might ask what the harm would be in making it invisible.

But let's generalize the question to SOME-FUNCTION where the key point is that at the time you write it, you haven't thought of a meaningful result for it.

Catching Erroneous Assignments

Returning an "ornery" value helps catch mistakes more easily when a meaningless result is accidentally used.

But with invisibles, we might adopt a philosophy like C's, and say you cannot put a void function directly after an assignment:

int x = some_void_function(...);  /* compiler will error */

Interestingly, C lets use them in comma-delimited expressions so long as they are discarded...you have to use parentheses to do it:

int x = (some_void_function(...), 10 + 20);  /* this is okay */
int x =  some_void_function(...), 10 + 20;  /* this errors */

We could do something similar

x: comment "hi" 1 + 2  ; this could error
x: (comment "hi" 1 + 2)  ; this could be okay

That's a little bit of a bummer for comment, but not the end of the world.

However, I want to see Ren-C's void/invisible used for things like BREAKPOINT and other tools... where having to rearrange your code in this way could be a nuisance (or dialect-wise, the GROUP!s could have semantic implications, e.g. if the code was inside of a COMPOSE).

But there are other issues...

Limiting Interface Flexibility

If at the time of writing a function you know that it doesn't have a meaningful return value, then making it void--instead of returning a trash value--ties your hands in changing it.

People will start writing things like:

all [
    ...
    some-function ...  ; user assumes no effect, because invisible
    ...
]

But if SOME-FUNCTION had returned a trash value, then they could have gotten the same effect more obviously with:

all [
    ...
    elide some-function ...
    ...
]

This also gives more freedom to change the interface later, if you think of an interesting value to return. You can progressively add more return types after the fact. But once people assume you always return void, this trap will happen...you're locked in forever in a way that was pretty much completely avoidable.

In PRINT's Case, a Differentiated NULL Output Has Value

Having a return value from PRINT that is either an ornery value or nothing lets you offer the neat option of returning NULL if the result of the print was nothing.

 >> line: "some text"
 >> print line then [print "We had output!"]
 some text
 We had output!

 >> line: _
 >> print line then [print "We had output!"]  ; no-op

To do this kind of thing requires having an output value.

I might be convinced that how people want to use PRINT is universally enough that they would rather it be invisible. But that would involve a very specific understanding of a very common function...similar to how elide and comment and assert and -- are known to have no result.

I don't think the average "no meaningful result" function fits in this category, and I'd say I'm fairly skeptical if PRINT belongs in it.

I Think "Void" Functions Should Be Used Sparingly

I think the feature should be discoverable, because how it's done is unique...and we are giving it a more normal name ("void function")

I'm feeling a bit more amenable to the idea that these are void return modes, even if it creates barriers to a multi-valued RETURN:

f1: func [x] [return]
f1: func [x] [return comment x]

But this means there needs to be a relatively comfortable syntax for returning a BAD-WORD! that is like what we're used to. I'm wondering if return none, recycling the word none for ~none~, is the right compromise.

UPDATE: I've found some other reasons why not to do this again. So I think arity-0 RETURN is likely going to keep the "return meaningless (and ornery) value" semantic.