Should append null fail, append BLANK! add nothing unless /ONLY?


The Rebol design concept that used to be discussed under the label “none propagation” has become a system of what I think are pretty good rules:

  • Blank input gives Null output…for non-mutating operations
  • Mutating operations do not accept BLANK! targets as input
  • Null input gives an error for operations that aren’t specifically about null handling
  • TRY can be used to “defuse” nulls into blanks

The reason this has merit is because it gives you a “hot potato” in the form of NULL, without going so far as to error in the moment. You get pinpointed error locality where a problem might be, and have an easy mitigation–if you want it–to use TRY to turn the null into a BLANK! and pass through all other results. And there are a host of null-driven operations to let you pick up the slack, e.g. first block else [code].

(I’ve suggested that if you ever actually wind up having to put a null state in a variable, test for null later with NULL? and act on it, that you probably could have done it better…reacting to the null without ever using it as a stored state. But it’s your choice, I guess.)

So this is being applied across the board. first _ is null…just like first [] is.

Consequences for APPEND/INSERT/CHANGE…

These rules affect a current feature of things like APPEND that I doubt many people have tried:

 >> append copy [a b c] null
 == [a b c]

>> append copy [a b c] if false ['d]
== [a b c]

When it was introduced, I thought it made sense. Since NULL is not a value you can put in a block, you don’t get confused about what that means. Then since BLANK! is a legitimate value that can appear in a block, you could append it:

 >> append copy [a b c] _
 == [a b c _]

But quietly accepting NULL loses the hot-potato benefit. If you say append foo third block and there is no third element in the block, doesn’t that seem like a good time to complain? Rebol code can pretty quickly get hard to read and debug, and wouldn’t it be nice if the person who wrote that line could articulate that there may not be a third thing in the block by saying append foo try third block?

This suggests making the default behavior of BLANK! to be to add nothing, and you need an /ONLY to override that. It may sound like a missed opportunity for null to exploit its out-of-band status. But realistically speaking, I don’t think purposefully adding blanks is all that common–certainly a lot less common than adding a block as-is, and that requires /ONLY!

No one probably cared (…yet…)

Like I say–most people probably were unaware of this, and so I don’t know there’s a lot of append block if condition […] out there today. We might need APPEND/OPT to be null tolerant, though the only reason to use it would be with /ONLY since otherwise you could say append block try if condition […].


So the answer, after delving and thinking on this issue, has come up as NO.

NULLs will continue to append or compose without error.

>> data: copy [a b c]
>> append data null
== [a b c]

>> compose [a (if false [<omitted>]) b]
== [a b]

But if you have a BLANK! in your hand, that is a “normal” value that will be COMPOSE’d in, or APPEND’d, etc.:

>> data: copy [a b c]
>> append data _
== [a b c _]

>> compose [a (_) b]
== [a _ b]

It’s your job to OPT something that might be blank, if you want blanks to be turned into NULLs and evaporate.

Sound a bit awkward, given how many routines return BLANK!s when they fail? Well, no worries…

NULLs are taking over a lot of former BLANK! roles

A failed ANY or ALL, a FIND that didn’t find anything, a MATCH that didn’t take… these will all be returning NULL, and not blank. This is the new return value protocol, where BLANK! is no longer used as a return value meaning “no match”. And it will be tolerable because NULLs will be conditionally false.

This gives BLANK!s a renewed purpose, but a purpose for which they should be thought of as “things”.

It’s a good feeling to pin down this previously oft-speculated-upon question, since it really was inelegant to have to say compose [a (opt any […]) b], etc. But we need wonder no longer…you will be able to omit the OPT because ANY will now return NULL!