Maybe the best way to attack the problem is to play out the sentences in your head:
"Hey, I'm curious, what does the PRINT function return? Does it return the string it printed?"
"No, that costs too much in the average case that doesn't use the generated output. It just returns a <your name here that's not void>
...so that you'll notice getting an error if you try to use the value. Unless it doesn't output anything at all--hence no newline--in which case it returns a NULL...so you can react to that situation with ELSE or THEN or whatever."
I firmly veto UNSET!... but am open to other suggestions. Though I've not had these strong issues with questioning VOID! and still am not really seeing the problem.
Maybe there's some word in the family of "signal" or "marker" you'd like better. But it seems to me that a big stamp with VOID on it is pretty well understood as a way of marking things as "alert, this thing is tainted, you might keep it for your records of what happened but don't try using it":
While VOID might be rethought, NULL is really non-negotiable
The parity with C's NULL (and all the languages that also embrace that meaning) is a critical element, and we can tick all the boxes: NULL is a 0 pointer value, it's falsey. It's unable to lead you to data to operate on (e.g. you don't have a "thing" you can set a NEW-LINE on, there's no thing). If a function returns NULL you are not stuck with a handle to an object that you have to free.
I can appreciate VOID as being more debatable
I could see the case that "VOID" wouldn't refer to a datatype at all, but the return behavior of functions like COMMENT and ELIDE. This interpretation would be really nothing... not a NULL, not an ornery value, but full-on vaporization. You might still phrase that as "The comment function returns void", or "has a void return", or something of this sort...as in C, when in fact returns don't even come into play.
While that has some appealing aspects (such as giving a seemingly plausible name for "the non-thing that invisibles could be said to return"), it has drawbacks:
-
Vaporization is a pretty unique phenomenon, and doesn't fit what C programmers or other people think of "void functions" as being for. They think of a "void function" more as one where the RETURN doesn't take an argument...the lack of a meaningful result is the reason for the distinction. In this vein, I don't think a plain RETURN should cause invisibility at the callsite, I think it should return this ornery type as it does today.
>> foo: func [x] [if x >= 304 [return], print "less than 304"]
>> if foo 1020 [print "likely misunderstanding of foo's return value"]
** Script Error: VOID! values are not conditionally true or false
; you wouldn't want that RETURN behavior to act like:
;
; if [print "likely misunderstanding of foo's return value"]
-
Taking void for vaporization would solve one naming problem, but we'd be stuck finding a name for the ornery type. I've explained at length why I don't like UNSET! as that name... because variables are unset, not values. (But I'm now comfortable with ~unset~
as a named purposeful instance of this WORDish thing). POISON! or TRASH! or other things are awkward and I think maybe too pejorative...it indicates that there's some kind of problem, when there's only a problem if you try to use it in certain ways.
I really do feel the overall usage in computer culture is that most people think of a function that returns no useful result, and they call that void. They don't imagine that function actually vanishing out of the evaluation stream.
So when all this is taken in, it feels like VOID! is the right name for the type. You have to explain it in terms of the void stamp... it's basically a WORD!, but it's been marked as a hot potato.
...er, so, maybe BAD-WORD! ?
Well, it's kind of in the WORD! family now...at least, you can interconvert voids and words. I don't know that you'd want it actually be in the category:
if any-word? x: second [10 ~bad~ 20] [
print "Is this a good idea to have it run?"
]
I don't know if that's a smart or silly concept. Maybe it would concretize it, and allow VOID to be used for eliding...though I can't say from here whether you're causing more problems than you solve with it.
(I've also not decided if these voids/bad-words should be able to carry bindings when they are converted or not. You can't use the binding, but they could still have one you could use if you converted them to a regular word...)
I guess you have to think about how it plays out in explanatory conversation, and if you like it.
"Hey, I'm curious, what does the PRINT function return? Does it return the string it printed?"
"No, that costs too much in the average case that doesn't use the generated output. It just returns a bad word...specifically the bad word ~nothing~
...so that you'll notice getting an error if you try to use the value. Unless it doesn't output anything at all--hence no newline--in which case it returns a NULL...so you can react to that situation with ELSE or THEN or whatever."
I feel like the whole point of reclaiming the word "void" here would be defeated if the "bad word" you got from running a function that returned nothing useful was ~void~
. You'd have to retake VOID for the "return disposition" of eliders and comments, or the whole renaming exercise was not useful.
This does line up to having meaning parity with C as far as the interface of "void" goes:
void rebElide(...); // this is the interface for rebElide, it "returns" void
REBVAL *rebValue(...); // this is when you want a result back to C
rebElide("print {rebElide means you avoid an API handle to a bad-word!}");
rebValue("print {if you wrote this, you'd leak the returned handle...}");
So we'd be saying inside the language, ELIDE also "returns void".
Further exposition on the experience:
>> foo: func [x] [if x >= 304 [return], print "less than 304"]
>> if foo 1020 [print "likely misunderstanding of foo's return value"]
** Script Error: the BAD-WORD! ~nothing~ is not conditionally true or false
There could also be a repurposing of the ~none~
word for this, since it's shorter (same length as void). But "null" and "none" are close words and it feels a little too close for my liking.
Well...maybe it could be ~trash~
? I guess there's nothing wrong with calling it what it is. You are "trashing" the output so the caller doesn't see it:
>> foo: func [return: <trash>] [
print "It's what this particular bad-word! application is used for..."
some-var: select block 'none-of-callers-business-what-we-assigned
]
>> if foo [...]
It's what this particular bad-word! application is used for...
** Script Error: ~trash~ is not conditionally true or false (bad-word!)
Other possibilities: ~opaque~
, ~nonya~

The idea of whatever this name is being the thing the console doesn't show is probably good. It could be ~hidden~
. Well, ~none~
isn't a terrible baseline...thoughts welcome.
Does That Resolve The Concerns?
There's enough novelty in teaching people how a true "void function" can be leveraged in this model, to not worry about disrupting any preconceptions about how void functions are used elsewhere. Then the alignment with the C interface is a nice bonus.
word!, set-word!, get-word!, lit-word! and bad-word! feel like a nice group. The "oooh... you said a bad wooooord" aspect is lightly entertaining and could make for some funny writing; it's easy to remember. Having "word" in the name helps mitigate the pejorativeness, so you can see there's at least some signal in it and know it has a spelling you can get...while still some stigma.
If it reconciles the concerns you have so we can move on to the non-naming-related design points, then it's a huge step forward, and I say we do it.