Accepting Void Parameters By Default: Why Not?

The choice to disallow VOID! parameters to functions is due to following a historical pattern set up by Rebol2 and R3-Alpha which wanted to stress their exceptional "fringe" status. I've cited Carl's remarks about this previously, in his blog post "UNSET! is not first class".

"It's important to understand the unset! datatype; otherwise, we run the risk of assuming that it is first class (assignable, passable, returnable) when it's really not intended for that kind of usage!"

Of course, it was all of those things (assignable, passable, returnable)...you just had to do a bit more work.

Ren-C has redrawn the map, in particular introducing NULL to take on the places UNSET! (or NONE!) was a "something" being used to mean "nothing". This moved VOID!s into the "definitely something...just a mean something" category.

We've also undone the R3-Alpha change that permitted GET-WORD! to access unset variables. This is important, and lets GET-WORD!s understood purpose be "This is a function, I want the function value and not to execute it" without getting more than you bargained for by tolerating typographical errors in the process.

Red threw out the "unsets are not conditionally true or false" aspect, and made them truthy. Here you see that combining with GET-WORD!'s promiscuity in a very unappealing way:

  red>> if :asdfaeiefiasdf [print "Double-Whammy"]
  Double-Whammy

But Ren-C keeps it.

Given these protections, and the revolutionary new interesting-ness of labeled voids...does forbidding VOID! to a function parameter that has no type annotation still make sense?

You already get basic protection by virtue of the fact that trying to access that parameter without GET/ANY will error. And those errors are now more informative:

>> print raxohatal
** Script Error: raxohatal is ~undefined~ (use GET/ANY to GET voids)

>> x: if true [null]
== ~branched~

>> x
** Script Error: x is ~branched~ (use GET/ANY to GET voids)

For that matter, does it really make sense to forbid NULL either?

For typed parameters, I do think it makes sense to exclude NULL from the ANY-VALUE! category.

But at this point, it seems like we'd be freer and more flexible if we just said an untyped parameter could take anything.

Seems like it's the right thing to do.

1 Like

I guess, that's what you get for not using type-annotations.

And it might strengthen the point, that functions without type annotations should only be used in throw-away scripts.

The one issue I note is more of a problem is error locality.

You'd see an error at the place the void is used, not at the callsite.

But as a further defense:

 foo: func [x y] [
     if x > 2 [print [y]]
]

One of the premises of VOID!s and erroring is "not yet". If I say x is 0, and Y doesn't get used, it seems like that's a "not yet" situation.

Anyway, errors need improvements across the board which I hope they will get. So I wouldn't get too stressed out about the locality, in the sense that "why should it be better for voids, but hard for everything else".

Just improve everything, and it's fine. :slight_smile:

2 Likes