The following is considered valid, but should it be?
for i in 1:3 4
print("hi")
end
# output: "hihihi"
This enables one-line for expressions without semicolons, of course:
for i in 1:3 print("hi") end
I encountered this potential issue when I temporarily replaced the upper index of the range but forgot to comment out the old range.
Perhaps semicolons should be required if attempting to place another statement on the same line as a for loop initiator? IMO would make the one-line code more clear.
I understand. In that case, would it make any sense, and not break anything, to require a return character after the for loop range and similar block declarations? (apologies if I’m abusing terminology)
While I don’t use one-liners, some people do and I don’t feel like enforcing my own style upon them (for one thing, my own style underwent quite a few changes already ). So I don’t think this should be invalid syntax.
However, this would be the perfect job for a linter, eg
for a in 1:10 print(a + 1)
print(a + 2)
end
could warn because it is mixing the two styles. Or one could ask the linter to warn about
for a in 1:10 print(a + 1) end
if an internal style guide for an organization considers it undesirable.
Perhaps it would have made sense to require semicolons. I don’t know why it was done this way, there’s probably a good reason. Several other keywords work this way too, such as if and while. However, let requires a semicolon:
julia> let a = 3 println(a) end
ERROR: syntax: let variables should end in ";" or newline
What’s more surprising to me is that not even a space is required before an end statement. This is perfectly valid:
julia> a = 3; while a > 0 println(a); global a-=1end
3
2
1
As for linter and warnings, looking again at the code that troubled you: for i in 1:3 4, it might be more effective to detect and warn about that independent 4, than requiring semicolons after keywords. (Or are there cases where a single number like that would make sense, unless when used as a return value?)
What I think can be said with certainty is that changing this would break an enormous amount of code, and would likely have to be done over a series of releases (like v0.7 and v1.0). I have used this syntax many times, especially when answering questions on this forum to keep my replies more compact – here’s an example:
@btime (s = 0.0; @simd for i in 1:10^9 s += i * 1.23 end; s)
As for linter and warnings, looking again at the code that troubled you: for i in 1:3 4 , it might be more effective to detect and warn about that independent 4 , than requiring semicolons after keywords. (Or are there cases where a single number like that would make sense, unless when used as a return value?)
The number was just for the example. In my real case it was a variable name, and I had overlooked it as a mistake when tracking down why it wasn’t working. E.g.:
for i in 1:3 nvalues # supposed to be 1:nvalues, temporarily replaced with constant
#dostuff
end
Well, same thing – would it make sense to just have an unused variable in your code? (Except when used as a return value, in which case it’s not unused of course.)
Just thinking whether it’d be better for a linter to focus on this, than the keyword syntax. Or perhaps both.
Nice find! But I don’t see what the ambiguity is, and how it’s different from for loops, do you? Since let assignments must be comma separated (also at the time of that commit), wouldn’t the only way to parse let x = 1 y = 2 be the let statement let x = 1 followed by the assignment y = 2? Which is confusing and could lead to bugs, but so could for loops:
julia> for a=1:2, b=3:4
println("$a, $b")
end
1, 3
1, 4
2, 3
2, 4
julia> for a=1:2 b=3:4
println("$a, $b")
end
1, 3:4
2, 3:4
I’m not really sure, but my best guess is that let predated for loops with multiple ranges. With a for loop, you knew that anything after the first assignment was in the loop body. But
let x = 1 y = 2
...
end
would be highly misleading. Basically, you expect let to have multiple variables and a for loop to have one. But yes, this is now inconsistent. Oh well.
I suspect changing this is likely to break a bunch of code. It’s easy to imagine people writing 1-line for loops like the example here. We can change it in 2.0 but it’s a slap-on-the-wrist kind of thing.