Literal Branching: Light, Elegant, Fast

It's uncommon to use expressions that evaluate to branches passed to conditionals. And when you do use one, you probably don't mind putting it in a GROUP! (especially considering that 99% of the time in the far more common cases you were willing to put it in a BLOCK!).

So Ren-C now uses that fact--plus generalized quoting--to allow for a briefer and faster way to evaluate to literals in your conditionals:

>> if okay '[block as data]
== [block as data]

Simply pass in a QUOTED! item of any kind, and that item will be what a branch evaluates to. It will be one less level of quoting than what you pass in:

>> if okay '<tag>
== <tag>

Previous attempts to get something like this used an /ONLY refinement. But this lets you mix and match in the same operator, as opposed to switching the operator into a "mode":

>> either okay '[1 + 2] [1 + 2]
== [1 + 2]

>> either null '[1 + 2] [1 + 2]
== 3

It Solves Some Problems for CASE

Historically, CASE was more lax in accepting types than the corresponding IFs would be:

>> case [1 = 1 <foo>]
== <foo>

It would allow the by-products of arbitrary evaluation to be used:

>> word: <foo>
>> case [1 = 1 word]
== <foo>

Sometimes this resulted in double-evaluation:

>> word: [print "surprise"]
>> case [1 = 1 word]
surprise
== true

The dodgy nature of this "may be a double evaluation, may be not" with no way to tell at source level raised some concerns, which are laid out in the "backpedaling on non-block branches" post.

The combination of soft literals and generalized quoting lets the same patterns that work for IF work in CASE. It lowers the risks in a legible way:

>> case [1 = 1 '<foo>]
== <foo>

It's Faster and More Efficient

Quoting is done with a byte in cells. So you can count up to 125 levels of quoting without really costing anything. (If you're wondering why not 255 levels, you need Quasi/Antiforms and the Dual state... read about LIFT_BYTE if you are curious)

So '[x] costs less storage (and has better locality with the surrounding cells) than [[x]].

Outside of the reduced storage, it's also lighter on the evaluator, because it doesn't have to push an evaluator frame to run the block!

Quasiforms Make Their Antiforms, Too!

For instance, you can make a SPLICE!

>> append [a b c] if 1 = 1 ~[d e]~
== [a b c d e]

>> append [a b c] if 1 = 2 ~[d e]~
== \~null~\  ; antiform

>> append [a b c] when 1 = 2 ~[d e]~
== [a b c]  ; just pointing out the when nuance
1 Like

Here's another compelling argument for literal branches:

>> 1 + either okay [2] [3] + 4
== 7

>> 1 + either null [2] [3] + 4
== 8

If you try that in Rebol2 or Red, you get:

red> 1 + either true [2] [3] + 4
** Script Error: + does not allow block! for its value1 argument

In Ren-C you can even use IF/ELSE interchangeably with EITHER. (This interchangeability now works 100% of the time):

 >> 1 + if okay [2] else [3] + 4
 == 7

 >> 1 + if null [2] else [3] + 4
 == 8

This might point to using literals for more things in the system where you think it is infrequent that people will be passing constructed meta-code in.

>> 1 + switch type of #x [issue! [2] tag! [3]] + 4
;... wouldn't it be nice if this were 7?

It's a bit touchy-feely to make that call. You don't want to have to say compose (reduce [...]) instead of just compose reduce [...], but you're looking to get a block out of it. How many evaluative infix operators want a block out of their left hand side? I think if your construct is explicitly used in meta-coding, you want to bias it to fitting in with other meta-coding without needing to put arguments in groups.

But things like CASE and SWITCH might benefit from soft-literalizing their case lists and switch lists, to favor the idea of fitting into more evaluative scenarios for using their product. While not being "a branch" per se, their lists are a kind of a "proxy for branches".

1 Like

So-called "literal branching" now runs up against the question of quoting's meaning for binding.

Generally we would assume the example above would mean you would get an unbound block (or, more accurately, the block with the binding it has... which is usually nothing if it's source).

This means it's semantically different from:

if okay [[block as data]]

Because there, the block is evaluated... and under that evaluation, receives the current binding.

It would instead be the equivalent of:

if okay ['[block as data]]

We can now consider the potential meaning of:

if okay $[block as data]

This could be the real equivalent of if okay [[block as data]].

$ forms aren't available for all types the way quoted forms are, but have the curiously appropriate property (in the current model) that they are available for all bindable types... so we can imagine the meaning of $ branches being "as is, bound".

1 Like

Possible Snag For Soft-Literal Branching?

That snag is the growing need for WRAP.

Previously it wasn't too common you wanted to pass an evaluated branch into a function. Now it might be common.

if some-condition [
    let x: ...
    let y: ...
]

=>

if some-condition wrap [
    x: ...
    y: ...
]

Literal branching requires you to put that WRAP in a group.

if blah blah blah (wrap [
    x: ...
    y: ...
])

Not the end of the world. But all things being equal, I'd prefer:

if (blah blah blah) wrap [
    x: ...
    y: ...
]

This isn't the first challenge to literal branching, but it's the strongest one yet.

BUT NO! THERE'S A REASON FOR STYLE 1

You don't want to pay for the WRAP if the branch is not taken.

Looks like literal branches are safe.


UPDATE: Revelation... FENCE! as WRAP:

FENCE!'s Higher Calling: WRAP