World: r4wp
[#Red] Red language group
older newer | first last |
Endo 25-Mar-2013 [6516x4] | How the outer REPEAT remembers the value i? red>> repeat i 3 [print i repeat i 2 [print [tab i]]] 1 1 2 2 1 2 3 1 2 |
And the last value is 2, not 3. red>> i == 2 | |
It is a bit confusing. | |
Even the outer REPEAT doesn't return its last value in this case: red>> i: repeat i 3 [print i repeat i 2 [print [tab i]] i] red>> i == 2 But normally it does: red>> i: repeat a 5 [4] == 4 | |
Rebolek 25-Mar-2013 [6520] | I don't want iterator word available outside of loop, this is really bad idea. |
Endo 25-Mar-2013 [6521] | I *feel* same way, I usually use i, j, k, t, s as /local words to functions, and I don't worry to mess them with iterator words inside function. But we should explain why or how it is a bad idea to Doc (if it really is) |
Henrik 25-Mar-2013 [6522] | What does this do (I don't have access to Red here): repeat i i: 3 [print i] |
Endo 25-Mar-2013 [6523] | red>> repeat i i: 3 [print i] 1 2 3 red>> i == 3 |
Henrik 25-Mar-2013 [6524x3] | hmm... yes, I guess that makes sense. |
but importantly, not necessariy for the same reason as in REBOL. | |
I guess we need consistency. Do any other loop functions in Red behave this way? | |
DocKimbel 25-Mar-2013 [6527x3] | All behave the same way. |
Bad idea is not an argument. You should look at it with a fresh view, if we got used to it after several years, it doesn't mean that it was a good design choice in the first place. | |
How the outer REPEAT remembers the value i? Iterator word value is stored on stack, recursively. | |
Endo 25-Mar-2013 [6530] | Doc: What do you think about the examples above? About return value. |
DocKimbel 25-Mar-2013 [6531] | Anyway, you shouldn't use the same word for nested loops, that is just confusing and will lead to errors. |
Endo 25-Mar-2013 [6532] | But if there is no extra binding, how does this work as expected? red>> repeat i 3 [print i repeat i 2 [print [tab i]]] |
DocKimbel 25-Mar-2013 [6533x2] | Return value: it looks logical to me, REPEAT returns the last value from body block. In your example, the last value in the last iteration is 2 (because `i` is set to 2 by the inner REPEAT). Same rule as for your second code example. |
how does this work as expected? I just answered that above: "Iterator word value is stored on stack, recursively." | |
Endo 25-Mar-2013 [6535] | I see. What about FOREACH, it looks it behaves different: red>> foreach i [1 2 3] [ ] red>> i == none |
DocKimbel 25-Mar-2013 [6536x2] | That looks like a bug to me. |
You should all think about the cost of a), such cost is paid in geometrical proportion in case of nested loops. Loops are at the heart of a language performances. Implementing it the R2-way (not sure how R3 handles it) would kill Red performances. | |
Henrik 25-Mar-2013 [6538] | If it's a stack, then there would be a small difference in letting the word pop, at the end of the loop, which makes it not available outside the loop, versus not popping it, leaving it available outside the loop, would there not? |
DocKimbel 25-Mar-2013 [6539x2] | It's the word's value that is stored on the stack by REPEAT (or other iterators), not the word itself. Words are part of contexts, that may or may be not stored on stack. Anyway, you can't add or remove a word from a local context in compiled code, at run-time. |
You need to created a new context on each REPEAT call (or use some hacked way of caching it) and you need to BIND the loop body (even probably BIND/COPY it to be cleaner). Now think about the costs of nested loops...and all that because you don't want to define another local word, that could anyway be added for you by FUNCTION. Think twice about the trade-offs. | |
Endo 25-Mar-2013 [6541] | I submitted the FOREACH issue on github. |
DocKimbel 25-Mar-2013 [6542x6] | Thanks. |
I find that the way Rebol handles it is more a limitation than an help. When checking for local words, you need to keep in mind two lists: one for the local words to collect, one for the iteration words, to leave apart. I prefer one simple rule rather than two rules. Making a few more keystrokes to add those iteration words to /local list is easier for me, than wasting brain processing power to tell apart which words I should collect, which one I should not. Moreover, if I want the iteration words to be defined in the function or global context, I can't in Rebol...Looks definitely more like a limitation than an help to me. | |
Huh, it seems that both current versions of R2 and R3 are not binding the loop body on each call to the hidden context: >> foo: func [code [block!] /local a b][a: 1 b: 2 repeat i 10 code] >> foo [a: a + b] ** Script Error: a has no value ** Where: foo ** Near: a: a + b I'm pretty sure I've seen it, maybe in older versions. Anyway, if current Rebol versions are not making that binding on each call, it makes most of my point a) irrelevant. So, you can forget about the binding cost. :-) Still the other concerns and limitation remain. | |
Hmm, my code is not testing the right binding... | |
>> foo: func [code [block!]][repeat i 1000 code] >> foo [i] == 1000 So both R2/R3 do re-bind the body block on each call. | |
Red would raise an error ("word has no value!") running the same code in the interpreter. You would need to write it this way to make it work: foo: func [code [block!] /local i][repeat i 1000 bind code 'i] That makes the required BIND operation explicit, so the same flexibility as in Rebol could be achieved but in a user-controlled way and only when needed. | |
Gregg 25-Mar-2013 [6548x3] | foo: func [code [block!]][repeat i 1000 code] foo [i] Doesn't raise an error in Red console for me. It works fine. Delcaring the word local to FOO does cause the error for each pass. |
My first reaction was the same as Endo and Bolek, because I'm used to the way it is. I rarely have to alias a loop counter for access outside the loop, and I like the language being smart enough to help me, so I don't have to declare these things all the time, or worry about leakage. However, my recent work on the idea of a new, general LOOP func (%mezz/new-loop.r here for those who didn't follow it) made me keenly aware of loop costs. I've only had a few instances where it really mattered, but I've still almost always avoided FOR, for performance reasons. Doc thinks things through carefully, and he has already said that FUNCTION could probably be smart enough to handle things for us, but we would have to consider how that works, to avoid environment dependent behavior. And how it affects very simple map/filter funcs. That is, do those one-liners now need /local specs. | |
Now is the time to discuss this, but it sounds like Doc has thought this through, and made his choice for clear reasons. If he's set on it, then we should look at what, if anything, needs to be done, to make Bolek, Endo, and others happy. | |
DocKimbel 25-Mar-2013 [6551] | Right, it doesn't error out because `i` gets bound to global context, but we want `i` to be local, which should be the common use case. |
Gregg 25-Mar-2013 [6552] | Maybe this is where something like my new LOOP could be used. It would be a general purpose func that trades performance for flexibility and automatic localizing of loop vars. |
DocKimbel 25-Mar-2013 [6553] | Gregg, I'm still open to counter-arguments. If you want the language to do the job for you, FUNCTION should be able to provide you that. |
Arnold 25-Mar-2013 [6554] | Compiling the console works out of the box as described on my Macbook. I am impressed. The Repeat by Endo does what I read it that it should be doing. The Doc: is it intentional? red>> repeat i 10 [ ] == 10 red>> i == 10 is what I expect it to be after the performing of the repeat. Unless you argue that i should only be valid inside of the repeat, but you should work with a function then not with a repeat. I do not see what is not to be liked about that? In REBOL and Red it is the human way that is leading not the programmers mind that is used to bend along with the computers view of the world. Again beautiful progress Doc. |
DocKimbel 25-Mar-2013 [6555] | Arnold, the point is that Rebol makes the loop counter a local word in a hidden context. It saves user from having to defining it as local manually (I have argued above about how I think this is, in practice, rather counter-productive). We need to find the right balance between human-friendliness and efficiency/productivity. |
Arnold 25-Mar-2013 [6556] | Yes, so i should not be defined after performing the repeat. There is something to say about that. On the other hand after performing the repeat, in my mind i is at the end of the range and thus has to be equal to the last value. Stupid humans ;) |
DocKimbel 25-Mar-2013 [6557] | so i should not be defined after performing the repeat. That's the Rebol way with the hidden context. In current Red implementation, such context is not used. |
Gregg 25-Mar-2013 [6558] | The biggest problem I see right now is that it binds to the global context, which will confuse existing REBOLers. I still think it can be solved cleanly, and the performance gain is probably worth it. And you know how I hate premature optimization. :-) |
DocKimbel 25-Mar-2013 [6559] | The loop word is like any other word bound to the global context unless you declare it locally. |
Gregg 25-Mar-2013 [6560x2] | Right, what I meant was that LOOP, being dialected, could easily localize the counter word. |
In %boot.red, UNLESS's second arg is called 'true-blk. Should it be 'false-blk? Or should both UNLESS and IF call it 'then-blk? | |
DocKimbel 25-Mar-2013 [6562] | Good catch, `then-blk` sounds fine to me. |
Gregg 25-Mar-2013 [6563] | For conditionals, do you want to use TRUE as a shortcut? Rather than... "If condition is not FALSE or NONE ..." "If condition is not TRUE ..." |
DocKimbel 25-Mar-2013 [6564x2] | Let me see... |
The shortcut version looks better. | |
older newer | first last |