Because of my earlier influences, I see a repeat loop as a atomic thing. Setup the parameters, and then it iterates. If the default is post, then it's post, leaving the matter of whether or not the index is "live" or static. Seems to me, either it's all live, or it's all static, or it's inconsistent, right? Wouldn't want one where some things are evaluated, and others static.
So then, why bother with the syntax at the end? "to" doesn't really communicate anything new, where the while and until do communicate variations on the basic repeat, and are indented accordingly at it's level.
Suppose it could be there, but then again, following the idea of lean means adding that and managing that when a lot of simple loops don't require it. Amount of keystrokes input by developers goes up, with no real value added. That's what I mean by lean.
Honestly, I think this does follow "form and function", with a different set of priorities. You seem to default to the most consistent presentation and behavior, where it appears chip was looking for the least input management to communicate the various behaviors. Given the small COG space for the language, is that unexpected? Secondly, given that small space, isn't it being fairly basic in capability, also unexpected?
Mind you, I don't think you are wrong in your desire for consistency. I just don't see where it adds value that some documentation wouldn't do the same. Finally, I think SPIN being lean is something not easily dismissed as a core design goal to understand why it is the way it is.
So then, not a bug. Just a design choice you would not have made. That's all.
I'm reading a book right now on higher level, top down, program design. (supposed to be simple too, but I don't know that yet) Weak area for me that I need to work on.
Anyway, in that book they list a few basic structures needed to do logic.
It just hit me that "while" and "until" are not really needed, and could be expressed as special cases of the basic repeat, which is all that is needed!
Wouldn't something like this (not tested) work just fine for a "while i is not greater than 5?"
repeat i from 0 to -1 step 0
'do.something
i >= 5
And I know. Bad. But hey! There's a use case for that boolean assignment operator we were discussing right there!
I'm just advocating the principle of least surprise, and I'm afraid that Spin's loop semantics deviate from that principle to a significant degree. For example, what would the casual programmer expect this loop to output first time around?
a:= 0
repeat i from 1 to a := 5
sio.dec(a)
Well, syntactically, a := 5 is the most immediate assignment to a, prior to the output, so the obvious answer is 5. The idea is that the less behavior that's hidden from someone reading a program, the better. Spin obfuscates too much behavior in this case.
Do you think limiting / exposing that behavior would have fit into the COG?
Secondly, no matter what is done, some people will write nice, readable programs, others won't. The value judgment comes down to whether or not enforcing that adds value overall.
I would suggest that Chip didn't think so, or was constrained by the COG instruction limit. Again, not saying you are wrong about it, just following the possible reasons for SPIN doing what it does. Also saying that there is value in SPIN doing what it does.
Do you think limiting / exposing that behavior would have fit into the COG?
Where there's a will, there's a way! Seriously, though, I doubt this behavior was the result of interpreter constraints, since the byte-codes are general enough to accommodate any kind of loop construct:
i := 1
temp1 := p~
repeat while i =< temp1
do stuff
++i
IOW, I'm sure these semantics were designed in, not squeezed in. It's just hard to make logical sense of it, and I think that the surprise expressed in this thread by experienced Spin programmers is testament to that.
Doh!! Yeah, don't know what I was thinking on that one. Whoops! Of course the byte codes can support lots of constructs.
(fetches coffee)
Edit: Hey, didn't he write that compiler in assembly language? Isn't SPIN PASM like? There is a correlation there, me thinks. Not sure he was thinking higher level as much as he was thinking accessible and that word lean again. Edit: It occurs to me that combination doesn't always equate to easy, or consistent.
Well, maybe we can ask Chip on the long Saturday night session at UPEW.
Might make for a very interesting conversation.
I agree with you. Designed.
I also do find all the little short cuts and modifiers difficult. But, as these discussions come up, talking through them always brings some improvement, and then it's back to lean again. A whole lot can be accomplished just by the order of things, and a few brief keystrokes. I know that "brief keystrokes" was a primary consideration, because Chip said it before, multiple times.
However, I do struggle with "hard to make logical sense of it". Make no mistake, the stuff discussed on this thread would (and probably will) trip me up, but...
Jazzed said it best, "think different". Lots of things about Prop and SPIN that are different. I guess the "should be statements" here are just value judgments, based on people's prior experiences. Chip had those too, and made his own right?
That's the logic of it, I suppose right there, with the rest simply being to understand those choices made.
Coffee? At this hour? I'd be in bed by now if I weren't waiting for a reply to an email I sent to a Chinese lens supplier. But, yeah, I enjoyed this thread, too. I know I can't change the way Spin works. But I'm still a couple stages of grief away from acceptance.
Yeah, I've had the thumbscrew put to me this month! Lots of prep work for concept proofs on complex software. My Prop time quota has been too small.
So yeah, coffee. I can enjoy some happy fun SPIN learning and discussion, while waiting for some software to grind through things. Could be worse!
I hate getting up early to bust through something like that. If it doesn't work, then I'm pinned, and I hate it, because usually I'm on stage after that happens, and let's just say I'm not ok with having to do either smoke and mirrors, or dance around some gaffe. Because I've been down that road, I'll do it early, nail it, then sleep a little, and do what must be done.
Or, if there is trouble, I've got time to deal, skip sleep, do what must be done, then bag out that day, sleep, and reset for the next.
At some point, my body is gonna say "no", but it hasn't yet!
I find the different perspectives many of us have very educational. That's why I will sometimes push back on something. Often really curious what the response will be. Learn something basic and good each time. Thanks!
Not to be too pedantic here, but I suspect you'll find the "bug" lies in the interpreter rather than the compiler. The compiler is simply poking the values you give it onto the stack and using the inbuilt repeat operator (assignment %-0000s10)
I've no experience with C#, but I'm sure the test must get performed before the loop executes the first time, as is done in Perl, which has a similar construct.
-Phil
That's true, it does evaluate the test before the first iteration. Therefore, it is possible for the loop to be executed zero times.
At least you do have repeat while/until ...
and repeat
while/until ...
which so far seem to be free of ambiguities. (I'm waiting in anticipation for a rejoinder!)
On the other hand, no matter what you think is the right way, the syntax, repeat x from m to n
will a priori be open to reading between the lines, unless what it actually does is made explicit by text or by example.
The way it does work is more like, repeat
while/until ...
as shown by your insight with the function call
repeat i from 1 to func
Given that it is going to execute the loop at least once, there is no reason for it to evaluate the limit until the end. True, it can be confusing to a beginner, or anyone as trap/trick, and is one of those things that leads to a practice of healthy skepticism.
You're right: while/until seem to work as advertised. I'm very disappointed with the repeat i from j to k semantics, though, as you can tell. I don't think their implications were thought out thoroughly enough. They could have been designed with less ambiguity and to abet more elegant idiomatic usage; but, as things are, that's off the table, requiring something more klutzy. My advice to anyone using this form would be as follows:
1. Steer clear of reverse looping and negative step sizes.
2. Use only constants and variables for the repeat arguments -- no expressions.
3. Do not change the iterator or target values within the loop.
Failure to abide by these guidelines will result in code that is ambiguous at best, misleading at worst.
-Phil
Addendum: It also occurs to me that failure to abide by rule 2 can produce code that is unnecessarily slow. I'm sure we've all done the following:
repeat i from 0 to strsize(s) - 1
Not only is the entire string scanned for its zero terminator after every iteration, the code will completely misbehave in the case of an empty string. It scares me to think how much code like this is out there. Now I need to go through all of my source to root it out.
As an antecedent, the BASIC Stamp 2 behaves in the same way:
FOR i = m to n STEP k
'''statements
NEXT
That is, the loop is always executed at least once, and n and the direction are evaluated each time around. The manual gives an example of swapping m and n at the end of the loop in order to automatically increment from m to n and then back down within the one loop. Another trick given as an example generates an arithmetic series by fiddling with the step size within the loop. I doubt if too many beginners were trapped by not RFTM, because the natural inclination is to use fixed limits and step. But I bet the designer (knowing who that is) was thinking about how to make the syntax as flexible as possible. The Stamp also shares the issue of speed if one uses an expression for n. It is evaluated every time around and in many cases there is no reason for doing so. The FOR:NEXT construct is slow enough as it is.
It should not be too surprising that the same flexibility (or foolishness, depending on your point of view!) is incorporated in the Prop. Personally, I like it to be flexible, but the main thing is to understanding how it works. I appreciate that you brought it up for clarification.
Moreover, Phil, you're going to have to go back and root through all your old Stamp code!?*$@
I think there's a distinction to be made between flexibility and mere cuteness or cleverness, especially when cute semantics collide with intuition. (And this from a guy who loves Perl! ) Moreover, I think there's a case to be made in the name of utility and idiomatic conciseness for a single evaluation of the limits at the beginning of the loop and for skipping the first iteration if the loop condition is not met at the outset. For those who want multiple evaluations of the loop limits, there's always while and until.
I've been thinking about this all day and have come to the reluctant conclusion that, in any subsequent incarnation of Spin, the current repeat syntax should be deprecated and replaced with one whose semantics better meet the needs and intuitive expectations of Spin programmers. It's too bad, too, because the repeat command syntax is so elegant. But the semantics are hopelessly screwed up, and you simply can't say, "Well, okay we fixed it: 'repeat i from a to b' means something else now." I haven't yet come up with a syntax as concise as repeat, though, which not only is frustrating, but makes me mourn even more the fact that repeat was squandered so casually on such inane semantics.
I've come up with some syntax additions that address the current repeat statement's semantic shortcomings. Rather than deprecating the repeat statement entirely, I think the addition of a couple keywords (upto and downto) should be adequate to fix the problem. Below are examples of the current looping and two new ones. The new ones could be implemented with the current interpreter, as the semantic interpretations demonstrate. The only existing Spin programs that would be broken by this change would be those which use the new keywords as constant, variable, or DAT symbols.
'--------------SYNTAX---------------------- --------------SEMANTICS---------------
repeat i from expr1 to expr2 step expr3 i := expr1
do stuff repeat
do stuff
if (i =< (temp1 := expr2))
temp2 := (i += expr3) =< temp1
else
temp2 := (i -= expr3) => temp1
while (temp2)
repeat i from expr1 [b]upto[/b] expr2 step expr3 i := expr1
do stuff temp1 := expr2
temp2 := expr3 'or 1 if step is absent
repeat while i =< temp1
do stuff
i += temp2
repeat i from expr1 [b]downto[/b] expr2 step expr3 i := expr1
temp1 := expr2
temp2 := expr3 'or 1 if step is absent
repeat while i => temp1
do stuff
i -= temp2
These new semantics eliminate the repeated reevaluation of the loop limit and provide for skipping the loop entirely if the loop conditions are not met at the outset.
Well this thread is a great help. My SPIN compiler would have broke any app that relies on this add behavior as I had expected that loops would behave in a manner consistent with other languages. My understanding of the descriptions in the Propeller manual did not point out this ambiguity to me.
I don't see the need for "upto" and "downto". Apart from being very ugly the only difference between them is the sign of the increment.
That is "i+=temp2" vs "i-=temp2".
Now temp2 comes from expr3, so why not just allow that the sign to be there?
I'm not even sure why the loop should ever stop if the iterator never hits the "to" value. That is the comparison should be "while not equal". If your step is such that the "to" value is never exactly reached then your loop never stops.
I was afraid that upto by itself with a negative step size would be confusing. If I could find a more universal keyword that's a synonym for to, I'd use it. Maybe until could do double duty. It's use here would not be ambiguous:
repeat i from a until b step c
Regarding your second paragraph, are you suggesting that if the loop variable misses the end value it should just keep going? I doubt that's the kind of behavior most programmers would want. I can think of many examples from my own programming where the limit is meant to be an upper limit for a non-unity increment. I guess the syntax could be exapanded, though, to accommodate both interpretations:
repeat i from a until i > b step c
repeat i from a until i == b step c
The more I look at it, the more I like the last examples. Heck, you could even incorporate while into that syntax.
-Phil
Addendum: Even better:
repeat i from a step b until <any condition>
repeat i from a step b while <any condition>
repeat i from a step b
do stuff
until <any condition>
repeat i from a step b
do stuff
while <any condition>
I don't think the current repeat-from-to statement should be deprecated. It is useful in cases where the bounds change within the loop. It's operation is a bit confusing, and it needs to be defined better in the Prop manual.
To be honest, I'm surprised how much attention this is getting. I think most programmers discover the weirdness of the repreat-from-to statement after programming in Spin for a month or two. This has been discussed a few times over the past two years that I've been on the forum.
A little off-topic, but when writing Sphinx, I ran into a problem differentiating between these two types of REPEAT:
REPEAT <rvalue>
REPEAT <lvalue> FROM ...
Spin is not LL(1) here IIRMCTCWIRD*, which makes recursive descent parsing difficult. I ended up having to... no, you don't want to know what I had to do. Things would have been so much simpler if the second REPEAT's syntax were (for example)
REPEAT FOR <lvalue> FROM ...
Or some other hint for my poor parser.
* If I recall my compiler theory correctly, which I rather doubt
The repeat syntax clearly says "to". If your code is such that the iterator never actually gets "to" the value specified it has no right to stop.
Heater, just for you, I'm going to propose an uptoandincludingbutnotbeyond keyword. Better?
Seriously, the choice of semantics -- regardless of the limitations of the English language -- should favor utility and brevity over a slavish adherence to the OED. Besides, if I say, "I'm going to Rocklin", and miss my turnoff, does that mean I have to keep going until I reach Cabo San Lucas?
Michael,
Did I hear you say "lookahead?" I've had to implement peek functions that retrieve the next token but don't advance the pointer, just for cases like this that recursive-descent can't handle natively. Sometimes, you just have to hold your nose and hope your sainted professors aren't looking over your shoulder.
repeat i from 0 uptoandincludingbutnotbeyond 10 step 3
Excellent idea:)
Which makes me think the Python programming language should have a syntax more like this:
thou count to N, no more, no less.
N shall be the number thou shalt count, and the number of the counting shall be N.
N+1 shalt thou not count, neither count thou N-1, excepting that thou then proceed to N.
N+2 is right out.
Once the number N, being the Nth number, be reached, then exit thou thy Holy Loop.
Here's another useful repeat form that could be implemented in the current interpreter:
----------------SYNTAX----------------- -------------------SEMANTICS-------------------
repeat i with ("AEIOU", 25) repeat temp1 from 1 to 5
do stuff i := lookup(temp1 : "AEIOU", 25)
do stuff
repeat i with ("0" .. "9", "A" .. "F") repeat temp1 from 1 to 16
do stuff i := lookup(temp1 : "0" .. "9", "A" .. "F")
BTW, the with keyword could do double duty, without ambiguity, to push a namespace onto a compile-time dictionary stack:
Comments
Because of my earlier influences, I see a repeat loop as a atomic thing. Setup the parameters, and then it iterates. If the default is post, then it's post, leaving the matter of whether or not the index is "live" or static. Seems to me, either it's all live, or it's all static, or it's inconsistent, right? Wouldn't want one where some things are evaluated, and others static.
So then, why bother with the syntax at the end? "to" doesn't really communicate anything new, where the while and until do communicate variations on the basic repeat, and are indented accordingly at it's level.
Suppose it could be there, but then again, following the idea of lean means adding that and managing that when a lot of simple loops don't require it. Amount of keystrokes input by developers goes up, with no real value added. That's what I mean by lean.
Honestly, I think this does follow "form and function", with a different set of priorities. You seem to default to the most consistent presentation and behavior, where it appears chip was looking for the least input management to communicate the various behaviors. Given the small COG space for the language, is that unexpected? Secondly, given that small space, isn't it being fairly basic in capability, also unexpected?
Mind you, I don't think you are wrong in your desire for consistency. I just don't see where it adds value that some documentation wouldn't do the same. Finally, I think SPIN being lean is something not easily dismissed as a core design goal to understand why it is the way it is.
So then, not a bug. Just a design choice you would not have made. That's all.
Anyway, in that book they list a few basic structures needed to do logic.
It just hit me that "while" and "until" are not really needed, and could be expressed as special cases of the basic repeat, which is all that is needed!
Wouldn't something like this (not tested) work just fine for a "while i is not greater than 5?"
And I know. Bad. But hey! There's a use case for that boolean assignment operator we were discussing right there!
Well, syntactically, a := 5 is the most immediate assignment to a, prior to the output, so the obvious answer is 5. The idea is that the less behavior that's hidden from someone reading a program, the better. Spin obfuscates too much behavior in this case.
-Phil
Secondly, no matter what is done, some people will write nice, readable programs, others won't. The value judgment comes down to whether or not enforcing that adds value overall.
I would suggest that Chip didn't think so, or was constrained by the COG instruction limit. Again, not saying you are wrong about it, just following the possible reasons for SPIN doing what it does. Also saying that there is value in SPIN doing what it does.
IOW, I'm sure these semantics were designed in, not squeezed in. It's just hard to make logical sense of it, and I think that the surprise expressed in this thread by experienced Spin programmers is testament to that.
-Phil
(fetches coffee)
Edit: Hey, didn't he write that compiler in assembly language? Isn't SPIN PASM like? There is a correlation there, me thinks. Not sure he was thinking higher level as much as he was thinking accessible and that word lean again. Edit: It occurs to me that combination doesn't always equate to easy, or consistent.
Well, maybe we can ask Chip on the long Saturday night session at UPEW.
Might make for a very interesting conversation.
I agree with you. Designed.
I also do find all the little short cuts and modifiers difficult. But, as these discussions come up, talking through them always brings some improvement, and then it's back to lean again. A whole lot can be accomplished just by the order of things, and a few brief keystrokes. I know that "brief keystrokes" was a primary consideration, because Chip said it before, multiple times.
However, I do struggle with "hard to make logical sense of it". Make no mistake, the stuff discussed on this thread would (and probably will) trip me up, but...
Jazzed said it best, "think different". Lots of things about Prop and SPIN that are different. I guess the "should be statements" here are just value judgments, based on people's prior experiences. Chip had those too, and made his own right?
That's the logic of it, I suppose right there, with the rest simply being to understand those choices made.
I enjoyed this thread actually.
-Phil
Yeah, I've had the thumbscrew put to me this month! Lots of prep work for concept proofs on complex software. My Prop time quota has been too small.
So yeah, coffee. I can enjoy some happy fun SPIN learning and discussion, while waiting for some software to grind through things. Could be worse!
I hate getting up early to bust through something like that. If it doesn't work, then I'm pinned, and I hate it, because usually I'm on stage after that happens, and let's just say I'm not ok with having to do either smoke and mirrors, or dance around some gaffe. Because I've been down that road, I'll do it early, nail it, then sleep a little, and do what must be done.
Or, if there is trouble, I've got time to deal, skip sleep, do what must be done, then bag out that day, sleep, and reset for the next.
At some point, my body is gonna say "no", but it hasn't yet!
I find the different perspectives many of us have very educational. That's why I will sometimes push back on something. Often really curious what the response will be. Learn something basic and good each time. Thanks!
That's true, it does evaluate the test before the first iteration. Therefore, it is possible for the loop to be executed zero times.
At least you do have
repeat while/until ...
and
repeat
while/until ...
which so far seem to be free of ambiguities. (I'm waiting in anticipation for a rejoinder!)
On the other hand, no matter what you think is the right way, the syntax,
repeat x from m to n
will a priori be open to reading between the lines, unless what it actually does is made explicit by text or by example.
The way it does work is more like,
repeat
while/until ...
as shown by your insight with the function call
repeat i from 1 to func
Given that it is going to execute the loop at least once, there is no reason for it to evaluate the limit until the end. True, it can be confusing to a beginner, or anyone as trap/trick, and is one of those things that leads to a practice of healthy skepticism.
You're right: while/until seem to work as advertised. I'm very disappointed with the repeat i from j to k semantics, though, as you can tell. I don't think their implications were thought out thoroughly enough. They could have been designed with less ambiguity and to abet more elegant idiomatic usage; but, as things are, that's off the table, requiring something more klutzy. My advice to anyone using this form would be as follows:
2. Use only constants and variables for the repeat arguments -- no expressions.
3. Do not change the iterator or target values within the loop.
Failure to abide by these guidelines will result in code that is ambiguous at best, misleading at worst.
-Phil
Addendum: It also occurs to me that failure to abide by rule 2 can produce code that is unnecessarily slow. I'm sure we've all done the following:
Not only is the entire string scanned for its zero terminator after every iteration, the code will completely misbehave in the case of an empty string. It scares me to think how much code like this is out there. Now I need to go through all of my source to root it out.
-P.
It should not be too surprising that the same flexibility (or foolishness, depending on your point of view!) is incorporated in the Prop. Personally, I like it to be flexible, but the main thing is to understanding how it works. I appreciate that you brought it up for clarification.
Moreover, Phil, you're going to have to go back and root through all your old Stamp code!?*$@
I think there's a distinction to be made between flexibility and mere cuteness or cleverness, especially when cute semantics collide with intuition. (And this from a guy who loves Perl! ) Moreover, I think there's a case to be made in the name of utility and idiomatic conciseness for a single evaluation of the limits at the beginning of the loop and for skipping the first iteration if the loop condition is not met at the outset. For those who want multiple evaluations of the loop limits, there's always while and until.
I've been thinking about this all day and have come to the reluctant conclusion that, in any subsequent incarnation of Spin, the current repeat syntax should be deprecated and replaced with one whose semantics better meet the needs and intuitive expectations of Spin programmers. It's too bad, too, because the repeat command syntax is so elegant. But the semantics are hopelessly screwed up, and you simply can't say, "Well, okay we fixed it: 'repeat i from a to b' means something else now." I haven't yet come up with a syntax as concise as repeat, though, which not only is frustrating, but makes me mourn even more the fact that repeat was squandered so casually on such inane semantics.
-Phil
These new semantics eliminate the repeated reevaluation of the loop limit and provide for skipping the loop entirely if the loop conditions are not met at the outset.
-Phil
I don't see the need for "upto" and "downto". Apart from being very ugly the only difference between them is the sign of the increment.
That is "i+=temp2" vs "i-=temp2".
Now temp2 comes from expr3, so why not just allow that the sign to be there?
I'm not even sure why the loop should ever stop if the iterator never hits the "to" value. That is the comparison should be "while not equal". If your step is such that the "to" value is never exactly reached then your loop never stops.
I was afraid that upto by itself with a negative step size would be confusing. If I could find a more universal keyword that's a synonym for to, I'd use it. Maybe until could do double duty. It's use here would not be ambiguous:
Regarding your second paragraph, are you suggesting that if the loop variable misses the end value it should just keep going? I doubt that's the kind of behavior most programmers would want. I can think of many examples from my own programming where the limit is meant to be an upper limit for a non-unity increment. I guess the syntax could be exapanded, though, to accommodate both interpretations:
The more I look at it, the more I like the last examples. Heck, you could even incorporate while into that syntax.
-Phil
Addendum: Even better:
I don't think the current repeat-from-to statement should be deprecated. It is useful in cases where the bounds change within the loop. It's operation is a bit confusing, and it needs to be defined better in the Prop manual.
To be honest, I'm surprised how much attention this is getting. I think most programmers discover the weirdness of the repreat-from-to statement after programming in Spin for a month or two. This has been discussed a few times over the past two years that I've been on the forum.
Dave
Yep. The repeat syntax clearly says "to". If your code is such that the iterator never actually gets "to" the value specified it has no right to stop.
- REPEAT <rvalue>
- REPEAT <lvalue> FROM ...
Spin is not LL(1) here IIRMCTCWIRD*, which makes recursive descent parsing difficult. I ended up having to... no, you don't want to know what I had to do. Things would have been so much simpler if the second REPEAT's syntax were (for example)- REPEAT FOR <lvalue> FROM ...
Or some other hint for my poor parser.* If I recall my compiler theory correctly, which I rather doubt
Seriously, the choice of semantics -- regardless of the limitations of the English language -- should favor utility and brevity over a slavish adherence to the OED. Besides, if I say, "I'm going to Rocklin", and miss my turnoff, does that mean I have to keep going until I reach Cabo San Lucas?
Michael,
Did I hear you say "lookahead?" I've had to implement peek functions that retrieve the next token but don't advance the pointer, just for cases like this that recursive-descent can't handle natively. Sometimes, you just have to hold your nose and hope your sainted professors aren't looking over your shoulder.
-Phil
repeat i from 0 uptoandincludingbutnotbeyond 10 step 3
Excellent idea:)
Which makes me think the Python programming language should have a syntax more like this:
BTW, the with keyword could do double duty, without ambiguity, to push a namespace onto a compile-time dictionary stack:
-Phil