Variant of ALSO that does not disrupt the non-NULL result


#1

The ALSO construct is the “anti-ELSE”. Instead of running and providing a result if the left hand side missed (e.g. was NULL), it runs when the left hand side was not NULL.

>> case [
       1 < 2 [<foo>]
   ] also [
       print "this runs"
       <bar>
   ]
== <bar>

So the ALSO is triggered by that non-NULL <foo>, but then runs and gives <bar>. What if we just wanted to run some code but not mess with the result, e.g. some kind of check? We’d have to use a lambda to get passed the result:

>> case [
       1 < 2 [<foo>]
   ] also x => [
       print "this runs"
       <bar>
       :x
   ]
== <foo> 

Can we come up with a good name for a construct that just does that? Run extra code if the left is not null, but pass through the result?

pass doesn’t really carry the full connotation (and even might sound like “ignore” vs. “pass-value-thru”). thus is weird, but a possibility…and it’s 4-letters like ELSE and ALSO and THEN. Though perhaps so could be used as a more abrupt/light version of also?

>> case [
       1 < 2 [<foo>]
   ] so [
       print "this runs"
       <bar>
   ]
== <foo>

A shorter, but related word. (?) Comments, critiques, better suggestions?


#2

Perhaps sub meaning subsidary to the main block


#3

Too close to subtract. (plus would be bad for similar reasons, and PLUS may become the prefix form of + for those needing a non-mutating word for ADD.)

I think I like so, because of its learnability. If someone has gone around and gotten used to ALSO, and then sees a SO, they would be drawn to the relationship between the two words as doing almost the same thing…but with SO being somehow “lesser”.


#4

I don’t think there’s any word which really conveys the meaning for where there is no existing english word. So, you might as well use a symbol.


#5

Since we’re trying to stick to ASCII, and Rebol has taken a lot of symbols already, there aren’t a lot of options…and we want to be careful about symbol use.

I’m probably happy with SO (which I came up while writing the post) unless someone has a noticeably better suggestion.

>> if 1 < 2 [print "if" <if>] so [print "so" <so>] else [print "else" <else>]
if
so
== <if>

>> if 1 > 2 [print "if" <if>] so [print "so" <so>] else [print "else" <else>]
else
== <else>

>> if 1 < 2 [print "if" <if>] also [print "also" <also>] else [print "else" <else>]
if
also
== <also> 

>> if 1 > 2 [print "if" <if>] also [print "also" <also>] else [print "else" <else>]
else
== <else>

Looks pretty solid to me. I hadn’t thought about using it with a plain IF, but I can see how it would be useful as an alternative to ELIDE. So instead of:

 if condition [some stuff elide (extra code)]

You could say:

 if condition [some stuff] so [extra code]

That’s a neat option.


#6

We could use ASCII 26. That’s not being used!


#7

Of course in Rebol2 that’s how Also worked

also (value1) (value2)

which returned the result of value1 but also evaluated value2

>> also (1 + 1) (2 + 2)
== 2

So, we could in R2

if 1 < 2 [also (print "if" <if>) (print "so" <so>)] else [print "else" <else>]

#8

Yes, that is how ALSO used to be, and it was unpopular for a reason. All such usages can be replaced with ELIDE, and the new functionality of ALSO has much wider application…as a way of running code after a match in any control construct.

Because the new ALSO will likely be used and beloved by everyone, I think that lends strength to SO as the “lesser ALSO” which runs code without disrupting the result.


#9

I have committed SO as the “Lesser ALSO”. The key argument for the name is that it is learnable…while I can understand someone seeing it for the first time might think “SO? What does that mean?” It is rather clear I think when its relationship to ALSO is understood:

>> 1 + 2 so [print "It's not null!..." 10 * 10]
It's not null!...
== 3

>> () so [print "It is null...won't branch" 10 * 10]
;-- null

>> 1 + 2 also [print "It's not null!..." 10 * 10]
It's not null!...
== 100

>> () also [print "It is null...won't branch" 10 * 10]
;-- null

It’s a nice way to do a little bit of code while not interrupting the result a conditional made.

typenum: switch type of data [
    integer! [10]
    string! [20]
] so [
    ... run a bunch of code here ...
    log "successful typenum calculation"
] else [
    fail "No match for typenum"
]

You can “telegraph” the result across some code you run, but only run that code in the “success” (non-null result) case. I think it comes together quite nicely.

I also think that if people look at the code for implementing this stuff, it’s getting nice and clean and relatively accessible.


#10

Pursuant to “Taking THEN back as the rightful complement to ELSE”, this is resolved by giving ALSO’s functionality back to THEN, and giving this “SO” construct the ALSO.

For this kind of operator that means we have:

  • THEN [branch] - If left hand side is null, evaluate to null. If the left hand side is non-null, evaluate branch and give its result…unless it is null, in which case turn it into a VOID!.
  • ELSE [branch] - If left hand side is non-null, evaluate to that value. Otherwise, evaluate the branch and give its result. Does -not- mutate nulls into VOID!
  • ALSO [branch] - If left hand side is non-null, evaluate the branch and discard the result. Return the null or non-null original value from the left hand side.
  • ELIDE (code) - Non-branching construct that will just run its code when it’s reached, passing through the result. Doesn’t appear to be infix, but the mechanics work that way, so it can be used in these chains.

What’s missing from the list is the “anti-ALSO”… a.k.a. “passthru ELSE”. It would only run its branch if the left hand side is null, but discard the result and pass through the null-or-non-null value. So “registering an interest that something didn’t happen, and wanting to do something, but really just wanting to propagate whether it happened or not down the chain”

But given these definitions, you can implement that with else [… null]. Because if the left hand side was non-null, the else wouldn’t have run. The only thing the branch needs to worry about passing through–if that’s what it wants to do–is the null. So this dovetails with the other rationale for why ELSE doesn’t mutate NULL results to void.

So… I think (hope?)… that this looks all squared away.


#11

…maybe otherwise ?