Why does failed IF return VOID instead of NULL?

When an IF (or any other control construct, CASE or SWITCH etc) does not take a branch, you get VOID back.

>> if 10 = 20 [<unreachable>]
== \~\  ; antiform (void!)

Since IF is not a "vanishable" function, ALL sees the void as a heavy void, so this errors in Ren-C... as heavy void is neither truthy nor falsey:

ren-c>> all [if 10 = 20 [<a>] if 3 = 4 [<b>]]
** PANIC: Empty PACK! cannot decay to single value

This is different from historical Rebol, where a failing IF was falsey:

rebol2>> all [if 10 = 20 [<a>] if 3 = 4 [<b>]]
== none

Given that VOID (can be) unfriendly to logic testing, would it be better to make it return NULL?

Given that VOID (can be) unfriendly to logic testing, would it be better to make it return NULL?

You're free to redefine your own IF however you like... :man_shrugging:

But I believe testing the result of an IF for truthiness is much less common than wanting it to vanish or opt-out of things:

>> compose [a b (if 1 = 2 ['q]) c d]
== [a b c d]

Were IF to return NULL, you'd get an error:

ren-c-null-if>> if 1 = 2 ['q]
== \~null~\  ; antiform

ren-c-null-if>> compose [a b (if 1 = 2 ['q]) c d]
** PANIC: ~null~ antiform cannot COMPOSE into lists

You'd have to somehow make this vanish using OPT to turn the null cases to VOID, or throw in empty branches:

 ren-c-null-if>> compose [a b (opt if 1 = 2 ['q]) c d]
 == [a b c d]

 ren-c-null-if>> compose [a b (if 1 = 2 ['q] else []) c d]
 == [a b c d]

 ren-c-null-if>> compose [a b (either 1 = 2 ['q] []) c d]
 == [a b c d]

ALL is Often a Good Replacement for "Nulling IF"

Very frequently you can just write, e.g.

>> all [10 = 20, <unreachable>]
== \~null~\  ; antiform

>> all [10 = 10, <reachable>]
== <reachable>

It's one of your many alternatives (along with either condition [...] [null] and all the other possibilities)... but in practice I think it tends to cover the desire pretty well.

1 Like