REBOL3 tracker
  0.9.12 beta
Ticket #0002063 User: anonymous

Project:



rss
TypeWish Statusbuilt Date19-Sep-2013 22:05
Versionr3 master CategoryNative Submitted byfork
PlatformAll Severityminor Prioritynormal

Summary Permit non-block values in the THEN/ELSE slots of IF, EITHER, UNLESS
Description Currently the conditionals require the branched code to be evaluated to be inside a block. This prohibits writing code of this form:

>> print either a > b {Greater} {Less Or Equal}
** Script error: either does not allow string! for its true-block argument

To do this, you instead write:

>> a: 10 b: 20 print either a > b [{Greater}] [{Less Or Equal}]
Less Or Equal

The rationale for this is that if the value were not in a block, it would be "live" in the interpreter and run regardless of whether the condition was met or not. So this is what would happen if you were allowed to pass non-BLOCK! arguments, e.g. values, coming from paren! evaluation or otherwise...

>> a: 10 b: 20 either a > b (print "Greater" 3) (print "Less Or Equal" 4)
Greater
Less Or Equal
== 4

At first glance it's a little scary. Then again, this is logically consistent with the rest of the way Rebol works... even if people aren't used to seeing *conditionals* that do this. It lays bare that IF, THEN, and ELSE are just functions like others and can process their arguments in the same way.

You can even do something cool with it... in this case, running both branches yet picking the result based on a condition. Still, the likely most common case would not run any code, but rather just pick values. Currently the idiom for this is:

>> a: 10 b: 20 pick [3 4] a > b
== 4

Which I've always found rather crazy, as here TRUE maps to 1 and FALSE to 2 in order to let it act like IF when you only provide one element in the block. :-/ (Note that pursuant to #2056, people should generally not think of Rebol as mapping booleans to *any* specific integers, given all integers are TRUE...)

Early on I would have thought not enforcing the blocks was a bad idea, due to seeming to encourage writing hard-to-read code. But I've been bitten a lot by forgetting to toggle whether I had a block on my condition, based on it transitioning from an IF to a WHILE (or vice versa). And given the code pipelines I've seen people write, I don't think allowing the option of using this feature is going to create any great catastrophe. No one is being forced to use it, and in the cases where you might use it, it's clean and handy.

Rebol's language philosophy is to encourage omission of "boilerplate" constructions (such as calling out function calls, and writing code in a way that it doesn't need parenthesization is also encouraged to allow for COMPOSE). Letting the block be the suppressor of evaluation--and teaching it as such from the beginning--would make Rebol a little more Reboly and cool.

In fact, I'd argue that it would be easier to teach the difference between evaluative contexts and unevaluative ones precisely with this example. Right off the bat, the difference could be demonstrated with parentheses vs. blocks...

As an added bonus, this is one change that wouldn't break any existing code.
Example code

			

Assigned ton/a Fixed inr3 master Last Update17-Feb-2014 23:45


Comments
(0003994)
BrianH
19-Sep-2013 23:26

It wouldn't break everything, but it would definitely break stuff. This proposal only changes how these functions behave when passed non-block values.

This would have exactly the same behavior that IF, UNLESS or EITHER have when passed block parameters - they would still be delay-evaluated, as they are now. However, this would break some semi-significant features that these functions have now:

  • Currently, the type specs of these functions perform runtime type checking to ensure that they are blocks. It is common in Rebol code to depend on these type specs to prevent code from executing when passed bad values. Any code that depends on this type checking would break. Such code would need to add calls to ASSERT/type or something.

  • By requiring the expressions to be wrapped in blocks, we write code that has those nice brackets which help us know where the expressions start and end. It's a readability issue.

  • As with all calls to these functions where the *-block parameters are generated rather than literals, they have to be generated with expressions that are evaluated every time, before the functions are called. This tends to be confusing in advanced code now, especially to developers who came from other languages. With this change, such expressions may end up being more common, even from newbies. This can also lead to newbie developers writing code where expressions have side effects and not understanding why those side effects are happening when they didn't expect them to.



It's a tradeoff, increased functionality for decreased debuggability in some cases.

On the other hand, the pick ["a" "b"] true? condition expressions can be replaced by the much more readable either condition "a" "b" format, which would also be faster. That gives us readability benefits, one of the benefits of the tradeoff.
(0003995)
BrianH
19-Sep-2013 23:30

Fork, PICK series logic! doesn't map booleans to integers, it uses booleans as an indexer. It's not a conversion, it's a different operation. In some cases you can pick based on entirely non-numeric indexers, like strings or words, it all depends on how the type can be indexed. Rebol is a powerful language.
(0003996)
fork
19-Sep-2013 23:44

@BrianH I know, hence why we're talking about MAKE LOGIC! 0 producing false while MAKE LOGIC! 1 makes true. However, I still think it's a pretty poor primitive, and more likely to confuse or cause bugs. While Rebol *can* do it, I don't think in this case it *should* do it because of how weird and counterintuitive it is, to support a head-scratching (on first glance) idiom.
(0004038)
abolka
5-Oct-2013 01:28

Sounds like a good idea to me; but I don't have a strong opinion on this.
(0004042)
Ladislav
6-Oct-2013 10:24

Yes, actually it is a clever and sound idea. It can make code writing more comfortable, making debugging slightly harder, probably (there is no free lunch).

In my opinion the decision should be left to a user poll or to Carl.

I would probably vote for the change exactly because it removes punctuation where I do not find it necessary.
(0004043)
Gregg
7-Oct-2013 07:49

I'm OK with it. As Ladislav said, it's a tradeoff.
(0004048)
endo
7-Oct-2013 13:25

It looks OK to me. Actually I sometimes think about it, would be nice especially for EITHER in PRINT EITHER value "yes" "no" case.
But I don't know if there are any BINDing issues for functions that return block! values. Or any performance issues.
(0004050)
adrians
7-Oct-2013 19:21

I like it. The paren evaluation of both clauses seems odd at first, but doesn't detract from the overall idea.
(0004052)
maxim
7-Oct-2013 20:44

As long as there are no measurable speed impacts on such high-use functions It's probably a good idea (it can even be back-ported to R2).

Using this allows us to merge conditional ops and conditional statements into the same concept, based solely on what is provided to the conditional. Much cleaner than other languages which have two redundant syntax.

IMHO the only real caveat, is that allowing any value after a condition will make some bugs extremely hard to track. Currently, when we have a missing block, we know exactly what is wrong and can fix it. With this changed... it may be quite a marathon to understand what went wrong.

ex: compose [ either a > b [ ] ( data ) ] ; this could be horrendously complicated to figure out, cause no error would be raised unless data is empty.
(0004214)
fork
12-Jan-2014 17:10

@Sgeo in StackOverflow chat suggested that in order to provide for a true replacement of:

PICK [x y] condition

There would have to be a refinement to IF, EITHER, and UNLESS which would tell it not to evaluate the branch.

>> pick [[1 + 2] [3 + 4]] 1 < 2
== [1 + 2]

>> either 1 < 2 [1 + 2] [3 + 4]
== 3

This suggests that perhaps an /ONLY refinement would be needed for this intention.

>> either/only 1 < 2 [1 + 2] [3 + 4]
== [1 + 2]

It is not likely something you would use unless the branches were not literals in the source

>> either/only (1 < 2) a b

Date User Field Action Change
17-Feb-2014 23:45 BrianH Status Modified submitted => built
17-Feb-2014 23:45 BrianH Fixedin Modified => r3 master
12-Jan-2014 17:23 fork Comment : 0004214 Modified -
12-Jan-2014 17:10 fork Comment : 0004214 Added -
7-Oct-2013 20:44 maxim Comment : 0004052 Added -
7-Oct-2013 19:21 adrians Comment : 0004050 Added -
7-Oct-2013 13:25 endo Comment : 0004048 Added -
7-Oct-2013 07:49 Gregg Comment : 0004043 Added -
6-Oct-2013 10:32 Ladislav Comment : 0004042 Modified -
6-Oct-2013 10:28 Ladislav Comment : 0004042 Modified -
6-Oct-2013 10:24 Ladislav Comment : 0004042 Modified -
6-Oct-2013 10:24 Ladislav Comment : 0004042 Added -
5-Oct-2013 01:28 abolka Comment : 0004038 Added -
19-Sep-2013 23:44 fork Comment : 0003996 Added -
19-Sep-2013 23:30 BrianH Comment : 0003995 Added -
19-Sep-2013 23:26 BrianH Comment : 0003994 Added -
19-Sep-2013 23:03 Ladislav Comment : 0003993 Removed -
19-Sep-2013 22:47 BrianH Category Modified Unspecified => Native
19-Sep-2013 22:44 Ladislav Comment : 0003993 Added -
19-Sep-2013 22:16 fork Description Modified -
19-Sep-2013 22:15 fork Description Modified -
19-Sep-2013 22:09 fork Description Modified -
19-Sep-2013 22:08 fork Description Modified -
19-Sep-2013 22:07 fork Description Modified -
19-Sep-2013 22:06 fork Description Modified -
19-Sep-2013 22:05 fork Ticket Added -