How to use ELSE and OR (and knowing the difference)


#1

ELSE and OR can be used for similar-looking purposes, but they have an important difference.

ELSE runs its right hand side when there’s a void on its left:

>> () else [print "left hand side is void, branch runs" 10]
left hand side is void, branch runs
== 10

>> false else [print "left hand side is not void, branch skipped" 10]
;-- no result

Whereas OR doesn’t tolerate voids on its left, and reacts to conditionally false things on its left:

>> false or [print "left hand side is 'falsey', branch runs" 10]
left hand side is 'falsey', branch runs
== 10

>> 20 or [print "left hand side is 'truthy', left hand returned" 10]
== 20

>> false or [print "both false means blank! result" false]
== _

>> () or [print "left hand can't be void"]
** Error: left hand side of OR can't be void

Here we see the behavior of OR when a BLOCK! is used as its right hand side. It is similar to an ANY, because it will give back the first value it calculates or a BLANK!. However, if you use a GROUP! for the right hand side, the result will be forced to a LOGIC!:

>> 1 or (2)
== #[true]

>> false or (_)
== #[false]

>> 1 or [2]
== 1

>> false or [2]
== 2

>> false or [false]
== _

(An additional constraint is that if you use a GROUP! on the right hand side, then should the right hand side be evaluated–e.g. it wasn’t a short circuit–it cannot be void. A BLOCK! on the right tolerate voids, treating them like blanks.)

In any case, if you have some kind of truthy-or-falsey producing thing…like ANY or ALL or PARSE, it’s a good candidate for OR. It can improve flow and readability, consider:

unless parse skip executable e_phoff [
    e_phnum [
        (mode: 'read) pos: program-header-rule
        (if p_offset >= offset [p_offset: p_offset + delta])
        (mode: 'write) :pos program-header-rule
    ]
    to end
][
    fail "Error updating offsets in program headers"
]

This starts looking a little better when OR is used:

parse skip executable e_phoff [
    e_phnum [
        (mode: 'read) pos: program-header-rule
        (if p_offset >= offset [p_offset: p_offset + delta])
        (mode: 'write) :pos program-header-rule
    ]
    to end
] or [
    fail "Error updating offsets in program headers"
]

For starters, it’s a little shorter. But more importantly, it lets PARSE stay at the forefront so this line of code looks primarily like a parse operation, as opposed to an unless operation. The infix OR helps contextualize things later, more than just seeing an unadorned block. You don’t have to mentally track whether it was an IF or an UNLESS when you’re reading at that point.

ELSE is for interacting with the outcome of conditionals, where the desirability of being able to return blanks or false is just too high:

 flag: if condition [
     false
 ] else [
     true
 ]

This couldn’t work with OR, because it would assume the successful condition check which produced FALSE would require triggering the next branch, and returning TRUE. So IF returns void when its condition fails, as a cue to tell ELSE that it should run. But more generally, it’s useful for making a failed conditional “opt out”.


The About-1/3-Of-2018-is-Over Report
#2

The idea of making AND and OR “conditional” as opposed to bitwise was fairly old, formally published as a “wish” on CureCode in 2011.

Implementing it required coming to terms with the fact that to be actually useful, it would have to be short-circuit. That happened fairly recently.

Being able to force an AND or OR to give back a LOGIC! is a fairly important feature. The last tweak of making the result behavior driven by the use of a BLOCK! or a GROUP! on the right was made today. I think this kills two birds with one stone. It gives a rule that can be predicted just by looking at the shape of the source. And it addresses that it looked a bit unnatural to write:

 all [
     ...
 ] or (
     ...
 )

…since all the other “branching” constructs used BLOCK!s.