Crystal: Constructing a tuple from the result of yielding a tuples elements, breaks yielding tuple elements everywhere

Created on 22 May 2015  路  8Comments  路  Source: crystal-lang/crystal

... or so.

Works as expected: http://carc.in/#/r/bp
Broken: http://carc.in/#/r/bt

bug compiler

All 8 comments

I think there are two bugs here:

  1. In the second example, in foo, you have commas after each yield. That should be a syntax error.
  2. So we can reduce it to this:
def bar
  t = {1, 3, 5}
  Tuple.new(
    yield t[0],
    yield t[1],
    yield t[2]
  )
end

bar do |j|
  pp j
end

Which incorrectly prints 5 three times. Let's try to reduce it:

def bar
  t = {1, 3}
  Tuple.new(
    yield t[0],
    yield t[1],
  )
end

bar do |j|
  pp j
end

Incorrectly prints 3 two times. What if we use an array for t?

def bar
  t = [1, 3]
  Tuple.new(
    yield t[0],
    yield t[1],
  )
end

bar do |j|
  pp j
end

Wait, what? Prints 3 two times again. What if we use regular vars?

def bar
  a = 1
  b = 3
  Tuple.new(
    yield a,
    yield b,
  )
end

bar do |j|
  pp j
end

Prints 3 two times. WAT?

Without vars:

def bar
  Tuple.new(
    yield 1,
    yield 3,
  )
end

bar do |j|
  pp j
end

3 two times. WAT?

In fact, it seems Tuple.new has nothing to do:

def bar
  foo(
    yield 1,
    yield 3
  )
end

def foo(*args)
end

bar do |j|
  pp j
end

At that point (or maybe two points before) I got the click and AHA moment! It seems the parser is seeing it as this:

foo(
  yield(1, (yield 3))
)

And then it seems that yielding inside a yield doesn't generate correct code. I tried this with Ruby:

def bar
  foo(
    yield 1,
    yield 3
  )
end

def foo(*args)
end

bar do |j|
  p j
end

It gives me a syntax error. And I thought I knew Ruby's grammar... In fact, just this:

def bar
  yield 1, yield 3
end

gives a syntax error in Ruby. @jhass Do you have any idea why?

(sorry for the long explanation, but I thought it was an interesting discovery)

Of course, as a workaround until we fix (and understand :-P) this, you can use parenthesis:

def bar
  t = {1, 3, 5}
  Tuple.new(
    (yield t[0]),
    (yield t[1]),
    (yield t[2]),
  )
end

bar do |j|
  pp j
end

Woah, good catch.

sorry for the long explanation, but I thought it was an interesting discovery

It sure was :D

http://carc.in/#/r/c1 This one might be related.

Oh, I see, we need to enter the macro language. Now I understand why it's in Tuple#clone :D http://carc.in/#/r/c2

A bit more about this, I found out that this gives a parse error in Ruby:

bar 1, bar 2

And also this:

bar(1, bar 2)

Apparently you can't have a call without parentheses inside another call unless it's the first argument?

They both parse fine in Crystal, though maybe they shouldn't.

I personally think the parsing of bar 1, bar 2 is fine. MoonScript, CoffeeScript, and Nim all allow it.

Yes, Crystal does it right imo (juxtaposition calls or whatever you'd call it)!

So maybe we can close this? Right now if you run the formatter on the Broken example from the original issue it gets formatted to this:

def foo
  t = {0, 2, 4}
  yield t[0],
    yield t[1],
      yield t[2]
end

def bar
  t = {1, 3, 5}
  Tuple.new(
    yield t[0],
      yield t[1],
        yield t[2]
  )
end

foo do |i|
  pp i
end

bar do |j|
  pp j
end

So, using an editor with an automatic formatter reveals the "problem".

And then it seems that yielding inside a yield doesn't generate correct code.

Still true, no? https://carc.in/#/r/z34

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asterite picture asterite  路  3Comments

nabeelomer picture nabeelomer  路  3Comments

relonger picture relonger  路  3Comments

lbguilherme picture lbguilherme  路  3Comments

will picture will  路  3Comments