How To Choose Between Returning VOID and NIHIL?

Ah-ha… I’d completely missed this use of void, as a return value.

Though this does now raise the question of whether a function should return nihil or void. Both seem to vaporise in lists, after all. I guess it depends on whether you want the function call to be ignored or not.

There's some touchy-feely choices made in the constructs regarding voids.

I think it's important that they're not truthy or falsey.

>> if void [print "This is important."]
** Script Error: Invalid use of void

Which rules out IF on an IF, since failed IF returns void:

>> if (if false [<a>]) [<b>]
** Script Error: Invalid use of void

However, ALL is kind of like a chain of IF statements testing for logic. Given the existence of NIHIL, should it demand that for opting out?

>> all [if true [1 < 2] if false [3 > 4] if true [5 < 6]]
** Script Error: Invalid use of void

If it wasn't willing to erase voids, you'd need to use ELIDE-IF-VOID to erase the voids:

>> all [if true [1 < 2] elide-if-void if false [3 > 4] if true [5 < 6]]
== ~true~  ; anti

But I feel like it works out better in practice if ANY and ALL consider void as no vote. Also, if all the expressions opt out of voting, the ANY or ALL overall is void.

>> any [void, all [if false [1 < 2] if false [3 > 4] if false [5 < 6]], void]
== ~void~  ; anti

But other constructs--like CASE--do not erase voids...only NIHIL. They have a "structure" to them, and it's desirable to not disrupt that structure too easily, e.g. by looking up a variable which could be void. (variables cannot be NIHIL, it's an unstable isotope)

I think it should be a very rare choice to make things return VOID unconditionally.

And I think it should be an even rarer choice to return NIHIL (e.g. because of the damage it can do to things like CASE or SWITCH structure). And when NIHIL is returned it should almost always be unconditional.

NIHIL is nice for debugging statements, because you can throw them in without disrupting the surrounding code

>> y: 10 + do [x: 1 + 2, dump x]
x: 3
== 13

Because of this, there's been some pressure to say that PRINT return NIHIL. So far, I've rejected that.

append [a b c] print "I think this should error"  ; rules out void

message: null
(print maybe message) else [print "I like reacting to no output"]

Also, though people reach for PRINT for debug output, I think it's poor for that. The fact that it evaluates blocks means I don't like the idea of print x taking too many kinds of input, that could one day become a block and surprise you by evaluating. So it's already a bad generic "debug dump". And logging with the same thing you use for committed output makes it hard to search for debug code and remove or disable it.

I like there being relatively few NIHIL-returning operations, and if you want to erase something use ELIDE.

But ASSERT returns NIHIL and that's neat in things like CASE or SWITCH for asserting something as true when you've gotten to a certain point:

case/all [  ; /ALL -> don't stop on first condition matched
    x < 10 [y: <lesser>]
    x > 10 [y: <greater>]
    assert [(x = 10) or (find [<lesser> <greater>] y)]
    x = 10 [print "You can imagine this kind of thing being useful"]
]

I should go through and replace these with real useful examples someday, but my hope is people get the point abstractly.

Anyway, I think it's nice to be able to do that particular thing without saying ELIDE ASSERT. A few other constructs make the cut of justifying NIHIL.

Hopefully it's clear why I don't think there are that many applications for functions that return nihil conditionally. ELIDE-IF-VOID is a very niche function that I don't think there are that many legitimate uses for. If you're going to come up with a construct that does return nihil conditionally, it should be discernible from the source-level syntax if it's a vanishing or non-vanishing invocation.

1 Like