It seems that after this discussion a trailing while statement support got dropped. Here is explained why. Here is the commit that did it.
The whole problem was that Ruby's implementation meant this
# This one first checks <condition> and then executes <code>
<code> while <condition>
# This one first executes <code> and then checks <condition> (this should've been removed)
begin
<code>
end while <condition>
Which was confusing because nobody expects the second version to run the code before checking the condition. Instead of removing the second possibility, which even the creator of Ruby would've wanted to be able to do, you guys decided to remove trailing while statements outright.
Your reasoning was that somebody with ruby experience might find the syntax confusing, even though a post-if works exactly the same.
Nobody thought that <code> while <condition> was confusing, it could've stayed. I use it a lot, and having to use 3 lines instead doesn't feel like Ruby's syntax anymore. It also makes porting stuff from Ruby to Crystal a pain.
In most languages something similiar would be achieved like while (<condition>) <code> but because Ruby doesn't support that in either it's if or while form it supports post-if and post-while syntax instead.
Matz never said he wanted to remove the whole option, just the second version of it. <code> while <condition> had never been an issue and should've been left alone.
In summary, there was never a problem with the <code> while <condition> syntax and just like <code> if <conditon> it's a core part of the Ruby syntax.
Honestly, you guys chose the easy way out here and it was the wrong option.
I know I am 5 years to late to this discussion, but I am hoping someone can add this feature back to Crystal.
The syntax lacks without it.
There is no problem with <code> while <condition> syntax, except that adding it would automatically also allow begin/end while <condition> with the same semantics, but different than Ruby's. The compiler then should make the parsing of the later a special case, and we don't like that either. Thus we decided, as explained in the FAQ, to not add that feature.
you guys chose the easy way out here and it was the wrong option
There is no such thing as "wrong" option. Designing a language is full of subjective decisions and this one was one of them. There are arguments you might agree or not, and that's fine. Nobody will ever reach 100% agreement on everything.
@waj If one allows the other, then why don't they work the same?
How did it ever happen that Ruby does this:
begin
puts "This runs once"
end while false
However with if instead of while
begin
puts "This doesn't print at all"
end if false
Would it be such a bad thing to bring back both methods of using trailing while, but make them behave like like how post-if behaves? Like Matz would've wanted with Ruby?
Would it be such a bad thing to bring back both methods of using trailing while, but make them behave like like how post-if behaves? Like Matz would've wanted with Ruby?
Yes, because then the behaviour would be different than Ruby. And for people coming from Ruby I think it's more confusing to have a different semantics than having a compilation error forcing to write the code differently
If we add a similar structure in the future, we could do it with a different syntax. For example (this is just me randomizing here, not something we discussed with the team or have a plan for):
puts "This runs once"
while false
and also:
puts "This runs once"
until false
This syntax would allow running code that must be executed at least once, but without having to resort on break statements, keeping the code structured. But my opinion is that I'd not add an analogous trailing syntax because they will be always ambiguous for the reader.
Anyway, this is probably not coming to the language anytime soon. At least not before 1.0.
Interestingly:
while true
p "."
end
# Same as
loop do
p "."
end
# Same as
loop { p "." }
In Lua:
while true do
print(".")
end
while is a keyword, so its semantics are different from other methods accepting blocks.
An idea, have loop accepting an argument:
# (I added a _ to avoid a conflict with the existing loop)
macro loop_(cond = true, &)
while {{cond}}
{{yield}}
end
end
i = 0
loop_ i < 10 { p i += 1 }
Even if my opinion on loop is it is quite superfluous (is while true really as long to write?). I rarely use it, but being already there, why not extending its syntax?
Yes, because then the behaviour would be different than Ruby. And for people coming from Ruby I think it's more confusing to have a different semantics than having a compilation error forcing to write the code differently
But I honestly think that this won't be the case, seeing as a lot of people that used Ruby already found begin/end while <cond> confusing beacuse it ran the code even when the condition resolved to false. Even in Crystal for the short time that that functionality was here people complained about it.
I think you won't find a single user who will complain about the syntax working correctly inside Crystal.
Either way Ruby code doesn't work the same in Crystal code. Because currently most of my Ruby code doesn't compile in crystal because I make a lot of use of trailing while. Which when I first saw it I though was a lot more confusing than I think anyone would be about begin/end while <cond> working like it should.
Edit: @waj you said one can't be added without adding the other, so I assume the parser sees them as the same. But then how can they behave differently in Ruby? Are you sure that you can't add one without adding the other?
If we'd just make the compiler disallow the second syntax, then the problem is solved right? No-one gets confused, and we don't have to remove a loved syntax feature that had nothing to do with the problem,
I'm closing this issue because it's "bring trailing while statements back" and that's never going to happen. If you want to have a way to do repeat { ... } while or similar, please open a separate issue.
Let me explain why it's never going to happen.
Your reasoning was that somebody with ruby experience might find the syntax confusing
That's me! :-) I found it confusing, and I have plenty of ruby experience.
In summary, there was never a problem with the
<code> while <condition>
Well, we (the core team members, and some others too) found it confusing. I always struggled with that in Ruby. It's just confusing. With exp if cond there's no confusion, there's no change exp is going to be executed before cond, it just doesn't make sense. With exp while cond there's confusion, so we decided to remove it.
But I honestly think that this won't be the case
How can you be sure? One thing is gut intuition, another thing is introducing a feature and later having to introduce a massive breaking change if that goes wrong.
I think you won't find a single user who will complain about the syntax working correctly inside Crystal
Me, @waj and counting...
That's why I'm closing this. But feel free to open a separate issue to discuss ways to do repeat { ... } while cond because exp while cond can be done right now: while cond; exp; end.
Alright so I think I understand the issue more clearly now.
Crystal, used to ALWAYS run the code once before evaluating the condition, which is obviously confusing as hell. Ruby only did that for instances where the statement was inside a block, which even Ruby users found confusing.
No wonder everyone was confused, every single person that came from Ruby had no clue why the hell Crystal was running their code. I mean that's like making <code> if <condition> run the code before checking the condition.
If you are using Ruby's syntax then why would you ever decide to change its behavior like that.
Anyway repeat <code> while <cond> sounds okay, that will still make a readable oneliner. However I still think that's adding an 8 character keyword for no reason.
Anyway if the dev teams do change your mind, I added a branch that implements while and until like they did in Ruby, on my fork.
I also want to point out that reading https://github.com/crystal-lang/crystal/issues/549 back,
@asterite seems to be the only person who thought Crystals implementation was right.
The other comments either wanted Crystal to make the syntax like Ruby's again (without blocks) or wanted the functionality to come back.
Most helpful comment
There is no problem with
<code> while <condition>syntax, except that adding it would automatically also allowbegin/end while <condition>with the same semantics, but different than Ruby's. The compiler then should make the parsing of the later a special case, and we don't like that either. Thus we decided, as explained in the FAQ, to not add that feature.There is no such thing as "wrong" option. Designing a language is full of subjective decisions and this one was one of them. There are arguments you might agree or not, and that's fine. Nobody will ever reach 100% agreement on everything.