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 ?!


??: infix 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
]

!!: infix 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
]

?!: infix 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 ++?

There's been a lot of changes here...

The concept of dashes for strings --[...]-- has pretty much taken this off the table.

I've evan started wondering if we might want to take plain double-dashes for "string literal to end of line".

>> foo: -- 4once think about writing like;;; literally anything &$#$&( here?
== --[4once think about writing like;;; literally anything &$#$&( here?]--

I don't know about that, but having an answer would be good. :-/

I like ? for OPT, and maybe ?? for OPT:VETO

Traditional OPT just erases the slot you're composing or reducing or whatever:

>> x: null

>> compose [a (? x) b]
== [a b]

But OPT:VETO will actually tell the operation you want to cancel something out at a higher level... like "Super opt out"

>> compose [a (?? x) b]
== \~null~\  ; antiform

This is turning out to be tremendously useful, and I think it goes naturally as a "more severe" OPT.

I have ideas that ! might be TRY, ?! could be OPT TRY, and maybe !! could be TRAP

It may be that these should all be left undefined, and people make up their own minds...

Yet I Think Differentiation Is Still Important

Saying your debug dumps have to be super short may be a false economy. Also, everyone has different styles of what they use more often or less often.

But I like them standing out in the code so you can see them.

Maybe what should be standard in the box is a little symboly, but less grabby of the lexical space.

What if dump were !dump ?

What if probe were !probe ?

What if breakpoint was !breakpoint or !break ?

We could say that when you're in the console you have shorthands without the exclamation points, but in source code it would be preferred that you use the decoration.

Anyway, A Shakeup And Review Is Needed

We might look at these things differently with a working debugger.