metaeducation / ren-c

Library for embedding a Rebol interpreter into C codebases
GNU Lesser General Public License v3.0
126 stars 27 forks source link

Labeled Voids (Note: use ~void~ instead of VOID) #1091

Closed hostilefork closed 3 years ago

hostilefork commented 3 years ago

This introduces a new concept that makes VOID! act something like a WORD! that stores a symbol.

Voids begin and end with tilde characters (this means that ~ is no longer a valid word character). Voids thus look like:

~undefined~
~void~
~empty~

The label of a void can be retrieved as a WORD! via LABEL OF

>> label of ~undefined~
== undefined

To make sure all labeled VOID! can be converted to WORD!, they cannot contain internal ~ characters.

If a void doesn't have a label (the only kind that does not have a word conversion), it appears as a single tilde. The label will be returned as NULL.

>> label of ~
; null

A void with no label will not be displayed by the console. But a conventional function return now produces ~void~, which will be displayed. This means functions like HELP have to say return ~ in order to suppress the visual.

Voids are still ornery in the sense that they cannot be fetched from WORD! or PATH! without GET/ANY, and they are not logically true or false but raise errors there. But this relaxes the handling of voids in other places, so they act inert in the evaluator.

The VOID generator function is removed. While it could be kept and return the default ~void~, confusions have historically arisen due to the fact that type of get/any 'void is ACTION! and not VOID!. When this drawback is combined with the more palatable and general literal form, then asking people to use that literal form lets them think about whether there is a meaningful name for their purpose. If there is no such meaningful name, they have the even more concise shortcut of just ~ to choose.

hostilefork commented 3 years ago

There's a lot to consider here, but I think it's another one of these "unmitigated success" commits.

As of course always, if people can come up with more tests or think of novel usages that would be great. There's only a few, but hopefully it will help you get the idea:

%void.test.reb

This will help people get their bearings, e.g. when working with mechanics like voidification of branches. I think that if you said help ~branched~ and could get to an explanation of what voidification is and how to work around it (e.g. use "as-is" branches like if condition @[null]) then that's a big leg up:

 (~branched~ = if true [null])
 (~branched~ = if true [])
 (~branched~ = if true [~overwritten~])

 (null = if true @[null])
 (~empty~ = if true @[])
 (~untouched~ = if true @[~untouched~])

(@rgchris -- if you're still out there -- I think this is about the best solution that the matrix of possibilities offers for that particular point -- which I know you found frustrating and you don't like @ anywhere but emails. But when you add it all up I think this is a winner. One has to weigh the complete picture. The completely non-valued NULL as a baseline result for if-and-only-if a branch ran is very powerful, and this gives you a per-branch override if you really need it.)

The one thing I'm thinking is that maybe the unlabeled void ~ should have the meaning of ~empty~, e.g. the result of do []. I figured we could see how empty felt for just a little bit first. When you hit enter in the console on a line with nothing in it, right now you get whatever do [] says... and we could either fix that to try not to run the code, but I'm thinking that it's probably best to say that's the natural base usage for the unlabeled void.