The VOID-in-NULL-out Protocol

In traditional Redbol, if you wrote a random chain like like first select block second options, there was the question of how you would manage the situation of any of these things failing.

People would request lenience... to say more operations should assume that if they got a NONE! input that they should just return a NONE! output. But this would give those chains no error locality... you'd get a NONE! at the output and not know what failed. The FIRST? The SELECT? The SECOND...?

You can see DocKimbel's response to a request that INDEX? NONE be NONE:

"There are precedents for built-in functions on series which yield none for none."

"Yes, but the less of them we have, the better, as they lower the robustness of user code, by making some error cases passing silently. The goal of such none-transparency is to be able to chain calls and do nothing in case of none, avoiding an extra either construct. In the above case, it still requires an extra conditional construct (any), so that is not the same use-case as for other none-transparent functions (like remove)."

But I Didn't Give Up So Easily...

The strategy cooked up for Ren-C is called "VOID-in-NULL-out".

There is an asymmetry created, in which certain functions take VOID as input, but then just return NULL out.

Then MAYBE can turn nulls into VOID.

Since (nearly) no function naturally takes VOID as an input, this creates a dynamic where you'd put as many MAYBE in a chain as you felt was warranted, if you expected any steps could fail. So perhaps first maybe select block maybe second options. A reader could tell which operations could potentially fail using this method.

It has shown systemic success:

>> case [false [[a b c]]]
; void

>> second case [false [[a b c]]]  ; "opt out" gives you a first step w/o error
; null

>> first second case [false [[a b c]]]
** Error: FIRST doesn't take NULL as input (use MAYBE to opt out via VOID)

>> first maybe second case [false [[a b c]]]
; null

>> first second case [true [[a [b] c]]]
== b

VOID is a Better Choice Than NULL, BLANK!, etc.

Prior to the rise of non-interstitial void, the strategy was tried as "BLANK!-in-NULL-out". But this created problems since sometimes blank was meaningful as input.

VOID has all the properties we're looking for, here. It's basically about as out-of-band as you can get.

  • It really isn't supposed to have meaning to any routine, so "no op" is about the best reaction to "hey, you got a void".

  • Like NULL, it can't be stored in blocks

  • Unlike NULL, it's not easy to get out of if you manage to generate a void, you probably did it on purpose.

  • There's a nice operator for turning NULL into VOID called MAYBE.

  • Control constructs return VOID when none of their branches run, so there's a bunch of functions you can draw from that effectively "fold the MAYBE in"

1 Like