Theory of Symbols and Repurposing `? ?` and `! !`

As I've said before: it's good to try new things--and Ren-C has plenty of ideas that turned out to be winners (or led to a path of evolution to something that was a winner). BUT if a change doesn't come out as a winner, it needs to be rethought and possibly backed out.

I think the use of ?? and !! as a kind of ternary operator has turned out to be a turkey.

These were chosen because they "stand out" a bit more than something like C's condition ? true-thing : false-thing; ternary operator. That is why Perl6 chose them over simply ? and !. Doubling that makes them stick out like a sore thumb.

For a time I liked it, studying the construct in isolation. If you pulled up examples they looked okay. Yet taking a step back from the page, they looked odd in context.

UPDATE: The facility that the ?? and !! operators used to provide have been replaced by soft-quoted branching. Really it is superior to have if condition [code] else '3 instead of the likes of if condition [code] !! 3. So hey, we tried a Perl-ism for a bit, and found a cross-cutting solution for all branching constructs that is better!

But can sticking out like a sore thumb be used better?

Hence, this property may be something you want to do with constructs that are designed to be transient, and not committed in code. This makes me think that ??'s previous purpose as a debug construct might have been superior. Then as you visually scan the code you can tell if you've left debugging information in--it jumps right off the page. By a similar token, !! seems like it might make a great breakpoint.

Note: Here was the old Trello card content about ?? and !!


>> 1 > 2 ?? [greater than] !! [less than]
== [less than]

>> 2 > 1 ?? [greater than] !! [less than]
== [greater than]

>> print ["Two is" (2 > 1 ?? "not") "less than one"]
== Two is not less than one

>> if 1 < 2 [print "they mix"] !! [<like> #this]
they mix

>> if 1 > 2 [print "they mix"] !! [<like> #this]
== [<like> #this]

>> select [a b c] 'b !! "no-match"
== c

>> select [a b c] 'd !! "no-match"
== "no-match"

"The ?? and !! operators resemble the function of C's condition ? true-clause : false-clause, but are much more flexible. They can be mixed and matched with other conditional constructs."

"Because !! tests for nullness on its left, it's a good fit for slipping in a default value when an operation returns nulls."

"Like AND, ?? tests for logic on its left. There is an ?! operator which tests for logic false on the left. But currently no version that tests for valueness on the left (e.g. a parallel to THEN)."

"Note that if you say 1 < 2 ?? 3 + 3 !! 4 + 4, both additions will be run. To "block" evaluation, there has to be a BLOCK! somewhere (or a GROUP! that is quoted), hence these are not meant as a generic alternative to AND and ELSE."

Note: Here was the old implementation code for ??, !!, and a false-triggered variant called ?!


??: enfix func [
    {If TO-LOGIC of the left is true, return right value, otherwise null}

    return: [<opt> any-value!]
    left [<opt> any-value!]
    right [<opt> any-value!]
][
    if :left [:right] ;-- will voidify result if right is null
]

!!: enfix func [
    {If left isn't null, return it, else return value on the right}

    return: [<opt> any-value!]
    left [<opt> any-value!]
    right [<opt> any-value!]
][
    :left else [:right] ;-- will *not* voidify result if right is null
]

?!: enfix func [ ;-- name suggests shorthand of `left ?? () !! right`
    {If TO LOGIC! of the left is false, return right value, otherwise null}

    return: [<opt> any-value!]
    left [<opt> any-value!]
    right [<opt> any-value!]
][
    if-not :left [:right] ;-- will voidify result if right is null
]

Having immersed myself into this mindset a bit, I am pretty much convinced of it. Because the idea of taking super-common debug routines and making them easy to type and spot is so compelling, I feel like PROBE has to be in that set. So I've been thinking this might be arranged such that:

  • -- is an alias for DUMP (now that we have the ME and MY operators)
  • ?? is an alias for PROBE
  • !! is an alias for BREAKPOINT (invisible)
  • ... is "TBD" (non-invisible/valued breakpoint, gives opportunity to resume w/value)

With "invisibles" like COMMENT and ELIDE rocking the scene (in a very good way), it's extremely nice having -- acting as DUMP did, and being invisible. Especially since DUMP treats strings just as labels.

all [
   some-condition
   -- "got past first step"
   some-other [12 (condition) 34]
   -- "got past second step"
   more-stuff #running <whatever>
]

If you were putting in throw away debug output, why would you ever use a PRINT? DUMP is flexible and can be made even moreso. Here you have invisibility (doesn't affect the ALL's logic), and an easy way of spotting when you've left the debug code in.

(Note: I've been wanting to make an argument that PRINT should probably only take BLOCK! and TEXT!, for reasonings very similar to why there was backpedaling on non-block branches. This might help bolster that argument, since casual dumping and printing of variables for debugging would have a superior solution.)

It's neat. So it feels like historical Rebol had a good thing going with this direction, and Ren-C has made that thing even better. Taking back -- for nobler purposes was a good step, and taking back == (see the IS and = plan) will be another forward step.

Apologies to Perl6, but we're now back to using zero ideas from perl again. :slight_smile:

Going further along these lines, I propose that ** not be infix power, rather that be just pow.

Remember that infix left-tight (the R3-Alpha way) grabs its immediate evaluated left argument:

>> power 2 2 + 1
== 8.0

>> 2 pow 2 + 1
== 5.0

And incidentally, that's the name of the standard C library function for power.

After seeing how use-it-every-day useful -- has become, hopefully this gets everyone thinking... what cool symbolic purpose might ** serve? How about ++?