If at first you don't SELECT, TRY...TRY again?

outdated-terminology
resolved
#1

We have a function that converts blanks to nulls...but passes through everything else. It's called OPT

>> opt 3
== 3

>> opt [a b c]
== [a b c]

>> opt _
; null

>> opt null
; null

While OPT is a bit more abbreviated than one likes to see in Rebol, it has precedent in PARSE. It's used frequently in English to discuss optional things. ("opt-in", "opt-out"). It has seemingly settled in comfortably in the <opt> parameter convention, which connects pretty strongly to the idea that if an OPT native did something, it would be related to that.

I've come to feel comfortable with it, personally. But I've never really felt great about its converse...the routine that turns nulls into blanks, but passes through everything else. We've been calling that TO-VALUE.

>> to-value 3
== 3

>> to-value [a b c]
== [a b c]

>> to-value _
== _

>> to-value null
== _

I actually called it TO-VALUE independently when I added it--but later saw a post long prior where BrianH had noticed its necessity and called it the same thing. (He did so despite the fact that R3-Alpha allowed you to put "UNSET!s" in blocks, meaning they were what I would call "values"...I think the name makes a lot more sense now that the word "value" is not applied to null. In implementation terminology, something that may or may not be a value is a "cell".)

As null has become the lingua-franca of "I didn't run that branch", it's needed somewhat frequently. But unlike OPT, I never grew to like TO-VALUE. It's long and ugly. It suggests a connection to TO, as if TO VALUE! were a thing, and it isn't (and won't be).

There's a lot of variations when you need this, none of which ever look particularly good:

case [
     ...
] else [
    blank
]

to-value case [
    ...
]

case [
    ...
    true [_]
]

The other day I came up with ran as a kind of query (did a branch run?) to get back LOGIC! true or false from a null or lack of one. But that doesn't help if you want to pass through the value in most cases.

While idly flipping through possibilities one word came to mind that has another use historically, but could be repurposed: TRY. (The old use of TRY, as in try/except has been rethought more eloquently as TRAP, I feel)

Once I thought of this, it began to make me wonder if this was the missing link from the push/pull over whether things like SELECT should "misleadingly" and automatically conflate not-finding-things with finding blanks. Early in Ren-C I'd changed it so that:

 >> select [a b c d] 'e
 ; null

 >> select [a b c d e _] 'e
 == _

This reflected--to me--the fundamental necessity of being able to distinguish these two states. If you're writing serious code, especially generic code like in the mezzanine or a library, this matters a lot. What @rgchris and @MarkEye managed to sway me on was that there's quite a bit of user code which wants to gloss over this difference. It's simply unlikely that that code would be putting BLANK! (or LOGIC! FALSE) in the data they're dealing with.

With the existence of CHAIN, I managed to assuage my systems-level concerns by implementing the lower levels in terms of names like SELECT*. Then, SELECT could just be a pipe of that "true select" into a "user friendly" select:

select: chain [:select* | :to-value]

But... might TRY be the bridge between readability/usability and technical coherence? Imagine putting SELECT back to being the "real select"...the one that gives null if nothing's there, vs. conflating with blank:

if x: try select [a b c d] 'e [
    ...
]  ; x will hold blank

if try x: select [a b c d] 'e [
    ...
]  ; x will be null

You have this ability to place the TRY at the point in the chain you want. And it's literately saying something: you know this could not find what you asked for, and you're okay with that. But a casual user will be protected, just as they are with using unassigned variables in general:

x: select catalog part-number-i-assume-to-be-there
print ["The part number is" x]  ; error vs. just churning ahead

It seems applicable across the board. try pick, try first, try switch... this short, descriptive word conveys your willingness to not find what you're looking for "disarm" the failure. And by using null and fitting into that convention, the doors are open to ELSE and ALSO and all the great things that are there.

x: select catalog part else [fail "part not found"]

Those conventions have changed the landscape, and the meaning and usefulness of null. DID has come to replace TRUE?/FOUND? and I think it feels good. I'd be happy to see TO-VALUE get the axe too, and be replaced with an elegant 3-letter word.

It could fairly easily catch old instances of TRY, by noticing when it was applied to literal blocks. So as conversions go it would be fairly safe.

2 Likes
NULL, first-class values, and safety