The Naming of NULL and VOID!

the difference between null and void is that null is a non-existent or empty value or set of values while void is an empty space — WikiDiff

I think nailing down the definitions of NULL and VOID outside of their current application or behaviour—even simply within English—would be instructive. I still feel unsettled with the way both work in a way I can't put my finger on even if I agree from other posts there is a need for both. Looking at the definitions in the link above, it feels at first read that our NULL and VOID are backwards, that NULL is the tangible placeholder that can appear in a block and VOID is the vacuum.

Either way, I feel do [] should vaporize:

reduce [do []] => []
1 Like

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~ :slight_smile:

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.

I get that NULL tracks to other language uses, perhaps my having no background in those languages is why I find it jarring (it's not inconceivable that they all use the word incorrectly :stuck_out_tongue:). I'm use to NONE as being control flow currency so the idea of NULL now having that role isn't that hard to grasp, just the other qualities that don't line up.

You're right in that I need to grapple with code, I need to review the test suite for the R3C branch where other changes aren't as big in comparison to NULL/BLANK/VOID. I should likely stop commenting on this issue until I've done that.

I do not foresee the NULL design point changing.

But if shifting the meaning of VOID as described above helps with your interpretation as "less than nothing" (as opposed to "a thing that has been marked invalid") then I'm game for that change. There seems to be a strong enough basis for it.

It just means having to coordinate on the replacement. What's the new name for the ornery result (is BAD-WORD! appealing at all to you?) and what is the name of the "typical" result that comes back from things like PRINT (e.g. ~trash~, ~none~, etc.)

Out of curiosity—would ~nan~ and ~infinity~ fall into your BAD-WORD! category?

There are distinct concepts of "signaling NAN" and "non-signaling NAN"

It may be that BLANK! is the quiet NAN, and math operators follow "blank in, null out". Some discussion of that point here:

I feel like if there were enough example code and articulation of what needed to be true about it, these decisions could be made in line with the desired behavior.

As I've had a little time to let it settle, it does feel solid to have "void functions" be those that truly return nothing--to the point of not putting any value in the evaluation stream. return: <void> thus takes this from invisibility, making rebElide() match ELIDE, as both returning void.

Then there's the case where a function has no meaningful return value, but acting void/invisible would be a source of misunderstandings and cause problems. It seems smoothest to have its spec say return: <none>. Then the result would be ~none~, which the console would suppress display of. (In the web UI, I was thinking the == ~none~ might start visible, but then fade out after a moment. I dunno if this can be done in a pleasing way...but if it could that would be neat.)

Although that's a new use of the word "none" vs. historical Rebol, we have to accept there are only so many short words to draw from. If length weren't an issue I'd like ~nothing~ better because it feels more like the right part of speech. But return: <nothing> feels wordy, you're going to see that a lot...and keeping it short has value.


BAD-WORD! conveys that something is a real value...a wordlike-thing that holds a spelling. I like that "bad" is short and fits with the other prefixes. It's communicative, and kind of funny, but not too funny as to be ridiculous.

The big question I see having to worry about is match any-word! some-function where you wouldn't really want BAD-WORD! to count. Maybe people would be forgiving if you just left bad-word! out of the any-word! set. I'm not sure if it's enough of a problem to lead one to favoring some name like TRASH! or JUNK!.


Any objections? This at least does seem to resolve the uneasiness with the use of a "greater absence" @rgchris was sensing out of void, and brings things into more alignment with C.

I like the way you're thinking here. I would like to discuss this in a broader sense in a future video meeting. There are opportunities to enhance basic user interactions and impart an illusion of awareness and intelligence-- as opposed to the rough-edges "80's cash-register" equivalent of interaction. Plus it ties into the "minecraft of programming" ethos. More on that later.