Taking THEN back as the rightful complement to ELSE


#1

Originally, the THEN operation was proposed by @rgchris as acting like an “anti-ELSE”. While ELSE was reacting to the outcome of a conditional based on the null-ness (it was called voidness in those days, now VOID! is something different)… THEN would take the reverse view. If something was non-null, then it would run.

This didn’t completely gel, because THEN seemed to fit into cases of being an enfix form of IF. Remember that “back in those days”, a failed ALL was still returning BLANK!, the following sensible-seeming code would cause a problem:

all [
    1 > 2
    3 > 4
] then [
   print "this would run due to BLANK! left hand side"
]

And that right there was one of the seeming best uses of THEN. So things were shuffled around, and THEN based its decision on the conditional truth or falsehood of the left hand side. The definition of then was actually just “infix IF”:

then: enfix :if

It felt a bit sad to have THEN and ELSE not “line up”. But the word ALSO was freed up and seemed like it could be ELSE’s complement.

It also felt a bit awkward to lack an anti-THEN. What would be the infix tester for conditional falseness on the left hand side, if it wasn’t ELSE?

But…wait! Things have changed!

Imagine we dial it back to where THEN is ELSE’s complement…driven by non-nullness instead of conditional truth.

  1. The motivating case of ALL […] THEN […] is now taken care of. ALL doesn’t return blank on failure, it returns null. There’s never any other falsey output from it, so it need not be conditionally driven to get conditional behavior.

  2. AND and OR have kept upping their game. If you need enfix logic detectors that run arbitrary code on their right hand side, you’ve got 'em. 1 < 2 and [print “This works, use a BLOCK! on the right vs. a GROUP!”]. (Remember, using a GROUP! on the right forces AND+OR to give back LOGIC!) Together these pretty much cover the puzzle of what to do about the left hand side being conditionally true or conditionally false.

  3. It would be preferable if ALSO could take over today’s “SO” behavior. e.g. “Do this thing also, but don’t add to the result”. SO is kind of a short word, and @gchiu didn’t like it.

The “weird” issue is still that 1 > 2 then [print “this will run”]. Of course, 1 < 2 else [print “this has been how it is already”]. But this can be learned by thinking of THEN and ELSE as being more concerned with whether something happened or not, in the abstract vs. the truth of that thing.

Which I will talk more about in an upcoming post, which is more speculative about applications of THEN and ELSE. But this is a good and right change that needs to be done…now!

(as a brief reprieve from JavaScript programming) :slight_smile:


Promises, Promises: Should THEN and ELSE be used for them?
Enfix Normal Revisited: Unlimited Default, One-Eval Operator?
Variant of ALSO that does not disrupt the non-NULL result
#2

I’ll point out that this definition permits something “wacky” that I had thought of a while ago, that I’m not necessarily suggesting for out-of-the-box experience. But neat to know it’s possible:

Imagine an arity-1 IF

Start with THEN and ELSE being null reactive, as outlined above. Now imagine IF is an arity-1 operation which “converts falseyness into non-existence”.

>> if 5
== 5

>> if true
== #[true]

>> if 1 > 100
;-- null

>> if _
;-- null

I’ve known we’ve needed an operation that converted falsey things to null, but not what to call it. Let’s imagine we did call it IF.

>> if 1 < 2 then [print "This works"]
This works

>> if 1 > 2 else [print "This works too"]
This works too

>> if 1 > 2 then [print "nope"] else [print "yep"]
yep

>> if 1 < 2 then [print "yep"] else [print "nope"]
yep

I like that form of IF…ELSE construct. This works all right with left-completing enfix. It all lines up.

But it’s verbose. The brevity of if 1 < 2 [print "less"] is good. And as we’ve seen with trying to convert EITHERs blindly into IF…ELSE, the rules of enfix can introduce their own complexities…so they should be used when they fit, not forced into every circumstance. (Though they do fit a lot of places!)

Yet it’s nice to see that this could work. It suggests things are fitting together well. If such things can work out, other things we haven’t thought of yet might too–that’s what composability is about.


#3

There wasn’t that much code affected, but here’s an example from SPLIT (which I’ve refactored over time):

switch type of dlm [
    bitset! [did find dlm try last series]
    char! [dlm = last series]
    text! [(find series dlm) and (empty? find/last/tail series dlm)]
    block! [false]
] then [
    add-fill-val
]

So this is a usage of the old meaning of THEN. It wasn’t interested just in whether the switch found a match. It wants to look at the result of the match’s branch evaluation and whether it’s true or false.

What I’ve said above is that we’re going back to where that isn’t a “THEN”. The THEN only responds to the fact that a branch ran, so the result is guaranteed non-null. Now if you want this logic response, you have to use AND:

switch type of dlm [
    bitset! [did find dlm try last series]
    char! [dlm = last series]
    text! [(find series dlm) and (empty? find/last/tail series dlm)]
    block! [false]
] and [
    add-fill-val
]

Compared to OR, it might be a little hard at first to grasp that the AND has a conditional/short-circuit nature to it. (And I guess you could launch the same complaint against THEN. What about the word “THEN” on its own sounds conditional? It can just kind of suggest “do this thing next”.)

But if the AND weren’t conditional, why would it be there? Why [...] and [add-fill-val] instead of just add-fill-val?

It may not be to everyone’s taste. I’ve noticed that JavaScript programmers are very into the use of short circuit, and their operators are uglier and nowhere near as flexible. Quoting from the answer there: “It works like this in pretty much all languages, but it’s become rather idiomatic in Javascript.”

(Note: Also crazy to find out… Visual Basic has not just non-short-circuit And and Or, but also short-circuited AndAlso and OrElse. I find that interesting in a way…especially that they chose AndAlso instead of AndThen. But I don’t think it scores points for this by making and-then and or-else.)

In any case, people who don’t like the short-circuit and for this kind of thing always have the fallback to use other things like a plain-old IF:

if switch type of dlm [
    bitset! [did find dlm try last series]
    char! [dlm = last series]
    text! [(find series dlm) and (empty? find/last/tail series dlm)]
    block! [false]
][
    add-fill-val
]

And with arity-1 IF, this would work like so:

if switch type of dlm [
    bitset! [did find dlm try last series]
    char! [dlm = last series]
    text! [(find series dlm) and (empty? find/last/tail series dlm)]
    block! [false]
] then [
    add-fill-val
]

(Which actually does sort of make an interesting point for arity-1 IF. It has a strength when your clause is separated from the IF by a distance. Though here, a casual reader might have a hard time telling that the THEN pairs with the IF, and not the switch. Anyway–worth pondering.)

In summary, it may seem a bit weird to use AND as a kind of “enfix IF”, but that’s what other languages do. And it brings the consistency to THEN that it needs, which covers pretty much most cases besides this kind.


The Siren Song of the Arity-1 IF