For loop is not working in crystal get an error

for i in 1..10
  puts i
syntax error in eval:1
Error: expecting token 'EOF', not 'in'

Crystal doesn’t have for loops, you would need to use the each method:

(1..10).each do |i|
  puts i
end

# or

(1..10).each { |i| puts i }
1 Like

then what is for used for in crystal ?

For loops are a thing, but only in the macro context: Macros - Crystal. The primary reason for the difference is macros are somewhat special in that they need access to the program itself for code generation reasons.

Related: Is there a reason why loops using the « for » keyword are not implemented in Crystal?

P.S. Could also do like:

1.upto 10 do |i|
  puts i
end
1 Like

In ruby I think the reason for is considered bad style is it makes a single variable with scope of the whole loop, for example if you do:

(1..10).each do |i|
    Thread.new { puts i }
end

the variable i is unique to each loop iteration, and it will work as expected. while if you do:

for i in 1..10
    Thread.new { puts i }
end

Maybe it will print all 10, as the threads don’t start until after it has iterated the whole loop, maybe it will print some numbers in between, who knows. It’s non-deterministic as all loop iterations are sharing the same variable i and it depends on when the threads run. golang is now changing their for loop semantics to avoid this problem.

In general I like the ruby and perl philosophy of more than one way to do things, when in different scenarios one way might express something more elegantly, or match closer with your way of thinking of the problem. But when it’s really two different ways to express exactly the same thing, and one has a significant downside, I totally approve that the crystal designers left it out.

1 Like

I guess, what you means is, for not introduce a new scope but each does via the block, the variable you use will be available outside of the loop when use for, it sucks.

Following is a ruby example:

x = [1, 2, 3]

for i in x
  puts i
end
#...
puts i #=> 3  # this probably not you wan

as the Thread example, when the new thread was created, both of them reference the local variable i, it can be any of 1,2,3. this issue often happen when use for in golang.

1 Like

No, I mean since macros are a means of generating code, if macro loops used a block then it wouldn’t be as simple for it to “paste” code into the program where they are expanded. Having it be {% for %}/{% end %} instead allows whatever is in-between to simply be inserted into the program upon each iteration w/o having to deal with the standard block syntax.

1 Like

Use the following code It should work as expected.

{% for i in 1..10 %}
  puts {{ i }}
{% end %}
1
2
3
4
5
6
7
8
9
10

If you want to know what really happened, use the crystal tool expand.

crystal tool expand -c a.cr:2:8 a.cr

1 expansion found
expansion 1:
   {% for i in 1..10 %}
     puts {{ i }}
   {% end %}

~> puts(1)
   puts(2)
   puts(3)
   puts(4)
   puts(5)
   puts(6)
   puts(7)
   puts(8)
   puts(9)
   puts(10)
2 Likes