Enforce '&' when the method yield a block

I find sometimes hard to know if a method yields a block when there is no type annotation, specially when the method is long, searching the yield keyword can be tedious.
Enforcing to add & will solve this issue:

def method
  yield
end

To change to:

def method(&)
  yield
end

Note: the API docs creates this type annotation (but still &block, not &)

5 Likes

I considered this as well. But does it really need to be enforced in the source code? It would be pretty easy to add this to the formatter, no big deal. But I’m wondering whether omitting the & should be a syntax error. It surely helps to tell apart yielding and non-yielding overloads.

If we do this, it should be added to the formatter after 0.32.0 (and maybe also a deprecation notice at this point). Then we can make it a syntax error in the following version.

2 Likes

This is a good idea and essentially what I also proposed to do in slow increments, starting with https://github.com/crystal-lang/crystal/pull/8117

1 Like

I would not enforce it in the code. But it could be used in the docs generator.

I think the point of the post is to actually enforce it in the code, and not only in the docs.
I also think it would be good to have in the signature

As said by @straight-shoota, a first step can be to set the missing & annotation in the code with the formatter, and in the API docs.
Then, see if the users would like to enforce it.
I don’t think it will be needed, most of Crystal code are formatted with crystal tool format.

One can add & to make it clearer, but I agree with @bcardiff in that I wouldn’t want to enforce it.

“enforce it” is a ambiguous: Does it mean to apply & as a default format or make missing & a syntax error?

None (edit: or well, both :-P). I wouldn’t make the formatter put it, not make it a syntax error. If you want to add it to make it clearer, do it. Same goes with type restrictions: if you want to add them to make it clearer, do it. But no enforcing please (it will add a lot of friction to the language).

I am just saying it would be helpful to have this annotation in the code, I don’t know if we want to enforce it or not.
At least, encouraging to have it, somehow (through the formatter for instance), would be great.

I think I’d also like to have this added by the formatter, because it helps to understand the purpose of a method. Whether a method yield or not is an essential property. You can’t call a yielding method without a literal block (an vice versa). IMO this should always be part of the signature so you can be sure when there is a & it yields and if not, it doesn’t yield.

@asterite I can’t see why adding this to the formatter would cause any friction. Maybe some people won’t like it, but that’s always the case. Other people won’t like it to not have this formatter rule.

1 Like

My main reason against it is that it adds noise:

def foo(&)
  yield 1
end

# vs.

def foo
  yield 1
end

Noise and lots of symbols in the source code reduces readability. If we enforce & then it’ll be all over the place, making the code less readable.

1 Like

The second method require more thinking, because everything is implicit.
We have to process that having yield 1 in the method block means def foo(& : Int32 ->).

1 Like

That’s three additional characters which in fact help readability, especially when the method body is longer than a single line.

I suppose we’re just used to yield being implicit and oblivious to the method signature. This has already derived from Ruby.
But as argued in my last comment I think it ought to be explicit in the method signature whether a method yields.