You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
even some long-time REBOL users were surprised seeing how short a REBOL implementation of that function may be
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:
less arguments than FOR has as Fork demanded making the interface cleaner
users can specify the comparison used as Endo demanded, not just in case when iterating over decimals
users can specify as many "cycle variables" as necessary like Bo demanded
users can specify the BUMP rule as Bo demanded
users can use COLLECT with it
; Example semantic design, would need to be native because of #539cfor: 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]]
There is no [throw] attribute in R3 yet (see #539), so mezzanine control or loop functions have problems.
Rebolbot commented on Mar 13, 2013:
Submitted by:BrianH
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 in 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.
Rebolbot commented on Mar 13, 2013:
Submitted by:Ladislav
"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)
Rebolbot commented on Mar 13, 2013:
Submitted by:BrianH
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.
Rebolbot commented on Mar 13, 2013:
Submitted by:BrianH
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."
Rebolbot commented on Mar 14, 2013:
Submitted by:Ladislav
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...
Rebolbot commented on Mar 14, 2013:
Submitted by:BrianH
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.
Rebolbot commented on Mar 16, 2013:
Submitted by:BrianH
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.
Rebolbot commented on Mar 16, 2013:
Submitted by:Ladislav
"It is likely a bad idea to catch any break or continue in the init evaluation code"
agreed
Rebolbot commented on Mar 16, 2013:
Submitted by:Ladislav
"Changed the name of the inc parameter to bump because it just as easily could be decrementing or doing something completely different."
Submitted by: Ladislav
The biggest issue is the name of the function. Cfor is probably too "ugly". Any better idea?
Advantages:
Imported from: CureCode [ Version: alpha 55 Type: Wish Platform: All Category: Native Reproduce: Always Fixed-in:none ]
Imported from: metaeducation#884
Comments:
Submitted by: BrianH
There is no [throw] attribute in R3 yet (see #539), so mezzanine control or loop functions have problems.
Submitted by: BrianH
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 in 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.
Submitted by: Ladislav
count me in, then, please.
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)
Submitted by: BrianH
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.
Submitted by: BrianH
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."
Submitted by: Ladislav
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...
Submitted by: BrianH
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.
Submitted by: BrianH
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.
Submitted by: Ladislav
agreed
Submitted by: Ladislav
good idea
The text was updated successfully, but these errors were encountered: