Dropping find?, while?, if?, unless? select?, etc

In the spirit of going over and reviewing various inventions, and deciding if they were good or bad, here's one I think is...a bad one.


Backstory: The idea of creating "question marked" versions of various functions originated with IF?. Due to the workings of historical Rebol's IF, it was not possible "from the outside" to know whether it took a branch or not. But some code tried to leverage the #[none]-returning nature of an IF that didn't take its branch, even though technically a taken branch could return none, false, void...

At one moment in time, there was a behavior change:

    r3-alpha>> all [1 (if 1 > 2 [3]) 4]
    == #[none]

   ren-c>> all [1 (if 1 > 2 [3]) 4]
    == 4

Ren-C was interpreting a failed IF as "opting out" of its contribution, as if it were not there. R3-Alpha/Rebol2 were tying together the failing of the condition with the idea that failure should propagate out.

I believed then (as now) that opting out was the more interesting behavior. But trying to answer every potential criticism, I added to conditionals like IF a /? refinement...which asked the native to return a LOGIC! of whether it took the branch or not, vs. returning the evaluative value of the branch.

There'd also been a history of people wanting to get LOGIC! variables out of something like FIND. They would use it even when it was not necessary, so they would write if found? find ... even though the IF was perfectly capable of dealing with a BLANK! to mean nothing was found.

Because CHAIN and SPECIALIZE had been invented just recently then, I was eager to test them. And so I was busy trying to show that you could make all these function variants easily while reusing the same frame. FIND? could be written in just a few lines, have all the same parameters, and return a LOGIC!... so if find? ... could take the place of IF FIND? FOUND....

---But I feel like I created a monster.---

People started using these in places they really weren't necessary, just to try and drive home that it was a test that could succeed or fail. Compare:

if all? [
    find? haystack needle
    select? data key
][...]

with

if all [
    find haystack needle
    select data key
][...]

Not only would the second case be slightly faster (by not having to go through a specialization layer), it looks cleaner. And as we learned with LENGTH?, if you just go throwing question marks everywhere they start to lose their meaning. Largely things that ended in ? were generally cheap tests with no side effects, and here we were starting to get WHILE? loops or IF?s that ran branches.

I think the whole point of nulls/blanks in Rebol is something that could be taken for granted and used as conditionally false. And these operations were a mistake to introduce.

For the cases where you actually do need a LOGIC!, besides TO-LOGIC we now have a sort of interesting word in the form of DID, as the positive analogue to NOT. Maybe it isn't perfect for all cases, but did select ... or did find ... or even did any [...] are all superior to SELECT?, FIND?, ANY?.

Moreover, I haven't really found all that many legitimate uses for the likes of IF?. Plus it's now possible to tell if a branch was or wasn't taken with did if condition [...] or not void? if condition [...] The kind of person who would be taking advantage of IF's properties in this respect has way more options if they just react to the output of the IF.

So I think we should retire these specializations.

They are cruft, and an example of gratuitous early use of specialization and chaining. It was nice that they were around in the early days of those abilities, and helped debug that and show what's possible. But they've overstayed their welcome, and need to go.

Four years ago, I introduced DID as a way to avoid having a bunch of logic-bearing variants of functions.

The functions in question you would use DID with typically would not return falsey values...although SELECT would be an exception here:

>> select [a #[true] b #[false]] 'b
== #[false]

>> did select [a #[true] b #[false]] 'b
== #[false]  ; but... but it did select!

What this suggests is that DID shouldn't really be an opposite to NOT, but a synonym for NOT NULL?

A further iteration of this problem has arisen...again leading to question marks...

With UPARSE growing up, it introduces the notion that you can actually return any result on a successful parse. This includes things like false, blank, and even null.

But if it returns something falsey, it returns the isotope form...to avoid the habit of people writing things like:

if uparse [#[true] #[false]] [some logic!] [  ; #[false] is final rule result
    print "This code errors, IF won't test a ~false~ isotope"
]

If you use THEN or ELSE, then all is fine...because they are isotope-tolerant and only react to true null.

uparse [#[true] #[false]] [some logic!] then [
    print "The successful return result here would be #[false]"
]

But not everyone likes writing code this way. Hence I fell back on the idea of a UPARSE? variant.

But what if DID was the isotopic tolerant form of NOT NULL?, and DIDN'T was the isotopic tolerant form of NULL?

This sounds like a big win to me. Now we can get rid of the prefix forms THEN? and ELSE? with much clearer answers.

As an interim measure, I'm making DID error if it gets a logic or a blank. This will help catch cases where DID was actually being used as a synonym for TO-LOGIC.

:sunglasses: