When you make a new thing, you wind up with more and more instances of that thing to inform its usage. And we now have a whole menagerie of left enfix normal operations. They are cool, but from using them regularly I notice aspects that are easy to get wrong...and may be hard for people to learn.
Hence it's important--as new information comes to light--to ask if there are improvements we can make. One question I've wondered is the balance of how much to evaluate on the left by default.
We first saw @giuliolunati's "dark corner"...the problem with telling enfix to "just complete":
return if x < 0 ["a"] else ["b"]
The first time ELSE gets an opportunity to run is when the ["a"] block is sitting to its left. By having a normal parameter type for its first argument (not
#tight), it says it doesn't want to take that block unless it absolutely has to. It wants "some evaluation", if it can get it.
If we take "some evaluation" to mean "as much evaluation as possible", then we're in trouble...because return if x < 0 ["a"] stands on its own. So if you run it, the ELSE never happens! With unlimited left completion, you would have to write:
return (if x < 0 ["a"] else ["b"])
There could also be some kind of operator, which could be a more convenient way of expressing this. Hand-waving the details a bit, let's just call it
return <- if x < 0 ["a"] else ["b"]
But if you don't want to have to resort to such a thing, you need a rule that stops the evaluation. This gave rise to the "one expression" limit. It will only evaluate one complete expression, and after that...no more. Since if x < 0 ["a"] is one complete fulfilled expression, it considers itself satiated and runs the ELSE before the RETURN.
When ELSE and company were the only left normal enfix operations, it was always able to complete an IF or UNLESS which provided a left boundary to "wrap" a condition wedged between it and the block. Yet with more operators like OR, AND, THEN...the condition wasn't wrapped. This meant seeing the other side of the coin more often. Consider:
not all [ something1 something2 ] then [ print "either something1 or something2 wasn't truthy" ]
Here the limit can bite you. THEN looks to its left, and because it's afraid to consume too much...it stops at the all [something1 something2]. You get:
not (all [ something1 something2 ]) then [ print "almost certainly not what you meant!" ]
If we stick with the limit-of-one-rule, you could use parentheses around the (not all [...]) to get the effect. You could also have an operator, but this time it would look like:
not all [ something1 something2 ] -> then [ print "either something1 or something2 wasn't truthy" ]
From the point of view of interpreter complexity itself, there's not an obvious winner. The evaluator has to have more or less the same code to support both models.
With completion by default, you can quickly develop a sort of "funny feeling" about passing parameters which use left enfix normal. If someone has been "trained" I think they'd read
return if x < 0 ["a"] else ["b"] and think "oh, my mind could complete return if x < 0 ["a"], so the evaluator would too!" Yet it feels a little more hidden to realize that not all [...] then [...] won't work when all [...] then [...] would. Fuzzy argument, I realize.
The stronger point, though, is if we're going to look at the two mitigating operators. I think the <- is easier to explain than the ->. And it seems like it's positioned more in the right place, where it should be. <- is saying "Hi, I'm the left parentheses of an implicit GROUP! whose invisible right parentheses is after the next full expression...including its enfix suffixes." The positioning of -> is weirder, and it's not as clearly a "put this there" kind of thing.
It isn't a major change to the code, but it significantly affects experience. Lately, I've been wanting more complex expressions with my ORs and ANDs and THENs... and hitting it a lot more often than encountering the "dark corner" when it was bent the other way. I have a feeling that letting it complete the left fully...and intervening with a GROUP! or operator when you don't want to...may be the better choice.