REBOL3 tracker
  0.9.12 beta
Ticket #0000884 User: anonymous

Project:



rss
TypeWish Statusreviewed Date3-Jun-2009 17:03
Versionalpha 55 CategoryNative Submitted byLadislav
PlatformAll Severityminor Prioritynormal

Summary General loop
Description 1) even some long-time REBOL users were surprised seeing how short a REBOL implementation of that function may be
2) the same users found out that as opposed to the mezzanine FOR function, the general loop implementation is fast even when written in REBOL

The biggest issue is the name of the function. Cfor is probably too "ugly". Any better idea?

Advantages:

1) less arguments than FOR has as Fork demanded making the interface cleaner
2) users can specify the comparison used as Endo demanded, not just in case when iterating over decimals
3) users can specify as many "cycle variables" as necessary like Bo demanded
4) users can specify the BUMP rule as Bo demanded
5) users can use COLLECT with it
Example code
; Example semantic design, would need to be native because of #539
cfor: func [  ; Not this name
    "General loop based on an initial state, test, and per-loop change."
    init [block! object!] "Words & initial values as object spec (local)"
    test [block!] "Continue if condition is true"  ; "true", not "TRUE", since it's conditionally truthy
    bump [block!] "Move to the next step in the loop"
    body [block!] "Block to evaluate each time"
    /local ret
] [
    if block? init [init: make object! init]

    ; It should actually make a selfless object from a block spec, but
    ; that's awkward to specify in mezzanine code, so just make sure
    ; that the native code makes a selfless context. It is likely a bad
    ; idea to catch any break or continue in the init evaluation code
    ; since the loop hasn't started yet, so we might want to just send
    ; them upwards. Or should we process break and ignore continue?

    test: bind/copy test init
    body: bind/copy body init
    bump: bind/copy bump init

    ; We don't need a direct reference to init anymore here, but we will
    ; need to make sure our new values are referenced on the stack for
    ; safety in the native so they don't get collected. Those assignments
    ; are metaphors for replacing the references to the blocks in the stack
    ; frame slots with references to their copies.

    while test [set/any 'ret do body do bump get/any 'ret]

    ; The body and bump should be evaluated separately to make sure their
    ; code doesn't mix, or otherwise you'll get weird errors. And don't
    ; forget to return the value from the evaluation of the body by default
    ; but also process break and continue from body, test and bump, according
    ; to the behavior of the while loop here.
]

comment [
    ; Example:

    cfor [num: 0] [num <= 30] [num: num + 1] [
        if num = 15 [recycle]
        print num
    ]

    ; Instead of incrementing the cycle variable you can e.g. double it
    cfor [i: 1] [i <= 2000] [i: i + i] [print i]
]

Assigned ton/a Fixed in- Last Update23-Mar-2013 17:05


Comments
(0000899)
BrianH
3-Jun-2009 17:17

There is no [throw] attribute in R3 yet (see #539), so mezzanine control or loop functions have problems.
(0003628)
BrianH
13-Mar-2013 19:44

I have a better idea of what to call this function: FOR. We need a procedural iterator/general-loop function, and since that's kind of an old school thing it really helps to give it a familiar name for people who have some experience in existing procedural languages. That means that the name FOR is actually the most important feature of the function.

People occasionally use R2's FOR because they need to do what it does; I use FOR in ad-hoc interactive code, for instance. However, nobody actually seems to like the function. I think that it is because FOR feels too much like it was based on something from FORTRAN, so it just doesn't feel right. If we are going to base a FOR loop on some procedural language then C's for () is a much more powerful model, would feel familiar to more people, and it looks better when translated to Rebol syntax.

We have a bit of an opportunity here, because like FUNCTION (#1973) even the people who actually use FOR don't really like it, so they're less likely to complain if we drop backwards compatibility. R2's FOR was also the slowest loop function, and has the most outstanding bugs, so most people didn't use it anyway even when it would have otherwise been a good idea. Even R3's FOR has outstanding bugs that were only discovered recently (#1993 and #1994), which suggests that people simply haven't been using FOR for anything serious in R3 either. Plus we've been talking lately about taking a bit more aggressive approach to backwards compatibility, which makes keeping functions like R2's FOR not quite as important. If we have a function that can do everything that FOR could do, but better, and do more, then we should seriously consider switching to it.

This function can do everything the old FOR can do, with no REDUCE needed because the init argument is basically passed to MAKE OBJECT! - if it were treated as a CONSTRUCT spec then it would need a REDUCE most of the time. It's also more powerful, since it can iterate over multiple words or arbitrary criteria. It can't be made as fast as the old FOR theoretically could be made, but the difference is very tiny and it makes up for it in power. It doesn't even need to be dialected, since feeling like a loop from an old-school procedural language is one of its most important features. It could use a tweak to make it even more powerful (have init optionally be an object) and some doc string tweaks, which I will do right after writing this comment.

Let's reserve the name EVERY for a declarative dialected pseudo-loop thing similar to list comprehensions. The rest of the names aren't really up to our standards. Call this one FOR.
(0003637)
Ladislav
13-Mar-2013 22:55

"I have a better idea of what to call this function: FOR." - count me in, then, please.

"they're less likely to complain if we drop backwards compatibility" - unfortunately, Sunanda seems to be one of the people that may complain (however, using arguments that convinced me he did not try this function yet not knowing what to expect)
(0003638)
BrianH
13-Mar-2013 23:02

First attempt at doc strings, trying to be consistent with the other loop functions. The top doc string might need work, but at least it's short enough. The other doc strings are better though, and reduce the amount of detail we need to put in the top doc string.

Added support for a premade object in the init parameter, because it might come in handy and doesn't break the model. Changed the name of the inc parameter to bump because it just as easily could be decrementing or doing something completely different. Slight tweak to the WHILE body in the example code, to emphasize that the evaluation of the body code shouldn't leak into the bump code. It'll all be native code anyway, but we want it to be the right native code. The usage example is unchanged.

Got rid of the "problem" status by switching this to native. It's only a problem if it's mezzanine.
(0003639)
BrianH
13-Mar-2013 23:04

Yeah, I covered Sunanda's objection with this sentence in one of my comments above: "This function can do everything the old FOR can do, with no REDUCE needed because the init argument is basically passed to MAKE OBJECT! - if it were treated as a CONSTRUCT spec then it would need a REDUCE most of the time."
(0003643)
Ladislav
14-Mar-2013 00:28

Regarding the MAKE OBJECT! call: that may be an oversimplification since it creates the 'self - containing context, which is not ideal. It seems preferable to use a selfless context...

If I remember it well I used MAKE OBJECT! when selfless contexts were not available...
(0003645)
BrianH
14-Mar-2013 02:24

Well, when you pass an object as init you'll have a 'self if it does. That's a downside to passing a premade object; the upside is that it's premade. If someone has a problem with 'self, they should pass a block spec. Updated the example code with a comment to make sure that block init specs should be turned into selfless contexts in the native version.

Maybe FOR should not BIND/copy the other code blocks either if init turns out to be an empty selfless context, just to save overhead since BIND is a noop in that case. The downside of this would be that the other concurrency and recursion safety side effects of BIND/copy'ing would be lost too (copying literal series before they get modified in the code block, for instance, like in a closure). That's too much of a downside because it makes BIND/copy unpredictably applied, when the developer might be depending its side effects.
(0003689)
BrianH
16-Mar-2013 17:33

Separated out the BIND/copy code so I could add comments about GC safety in the native and make sure it was returning the result of the body block evaluation. Also, added comments about BREAK and CONTINUE processing.
(0003690)
Ladislav
16-Mar-2013 22:28

"It is likely a bad idea to catch any break or continue in the init evaluation code" - agreed
(0003691)
Ladislav
16-Mar-2013 22:46

"Changed the name of the inc parameter to bump because it just as easily could be decrementing or doing something completely different." - good idea

Date User Field Action Change
19-Feb-2014 22:20 BrianH Comment : 0003628 Modified -
23-Mar-2013 17:05 Ladislav Description Modified -
23-Mar-2013 17:02 Ladislav Comment : 0003622 Removed -
23-Mar-2013 17:02 Ladislav Comment : 0003623 Removed -
23-Mar-2013 17:01 Ladislav Description Modified -
16-Mar-2013 22:46 Ladislav Comment : 0003691 Added -
16-Mar-2013 22:28 Ladislav Comment : 0003690 Added -
16-Mar-2013 19:10 BrianH Code Modified -
16-Mar-2013 19:01 BrianH Comment : 0003645 Modified -
16-Mar-2013 17:46 BrianH Code Modified -
16-Mar-2013 17:45 BrianH Code Modified -
16-Mar-2013 17:42 BrianH Code Modified -
16-Mar-2013 17:38 BrianH Code Modified -
16-Mar-2013 17:33 BrianH Comment : 0003689 Added -
16-Mar-2013 17:27 BrianH Code Modified -
14-Mar-2013 02:55 BrianH Comment : 0003645 Modified -
14-Mar-2013 02:37 BrianH Comment : 0003645 Modified -
14-Mar-2013 02:36 BrianH Comment : 0003645 Modified -
14-Mar-2013 02:35 BrianH Comment : 0003645 Modified -
14-Mar-2013 02:27 BrianH Code Modified -
14-Mar-2013 02:24 BrianH Comment : 0003645 Added -
14-Mar-2013 00:29 Ladislav Comment : 0003643 Modified -
14-Mar-2013 00:28 Ladislav Comment : 0003643 Added -
13-Mar-2013 23:04 BrianH Comment : 0003639 Modified -
13-Mar-2013 23:04 BrianH Comment : 0003639 Added -
13-Mar-2013 23:02 BrianH Comment : 0003638 Added -
13-Mar-2013 22:58 Ladislav Comment : 0003637 Modified -
13-Mar-2013 22:55 Ladislav Comment : 0003637 Added -
13-Mar-2013 22:52 BrianH Status Modified problem => reviewed
13-Mar-2013 22:52 BrianH Category Modified Unspecified => Native
13-Mar-2013 22:52 BrianH Code Modified -
13-Mar-2013 19:45 BrianH Comment : 0003628 Modified -
13-Mar-2013 19:44 BrianH Comment : 0003628 Modified -
13-Mar-2013 19:44 BrianH Comment : 0003628 Added -
13-Mar-2013 15:51 Ladislav Category Modified Mezzanine => Unspecified
13-Mar-2013 15:50 Ladislav Category Modified Unspecified => Mezzanine
13-Mar-2013 02:54 Ladislav Code Modified -
13-Mar-2013 02:52 Ladislav Comment : 0003623 Modified -
13-Mar-2013 02:51 Ladislav Comment : 0003622 Modified -
13-Mar-2013 02:49 Ladislav Comment : 0003622 Modified -
13-Mar-2013 02:47 Ladislav Comment : 0003623 Added -
13-Mar-2013 02:43 Ladislav Category Modified => Unspecified
13-Mar-2013 02:43 Ladislav Description Modified -
13-Mar-2013 02:43 Ladislav Comment : 0003622 Added -
3-Jun-2009 17:20 Ladislav Code Modified -
3-Jun-2009 17:17 BrianH Comment : 0000899 Added -
3-Jun-2009 17:16 BrianH Status Modified submitted => problem
3-Jun-2009 17:16 BrianH Code Modified -
3-Jun-2009 17:16 BrianH Description Modified -
3-Jun-2009 17:03 Ladislav Ticket Added -