-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Force calls with blocks to have their arguments parenthesised #5034
Conversation
I agree blocks |
Maybe go ahead with adding this to the formatter, but as an optional param? Then, after the compiler changes [and other applicable code mods] are ready, this feature could be made default? I am guessing that is basically what @RX14 intended? |
Perfect example to set my reaction to a definitive "hell no" :( |
Was this change discussed anywhere? what benefit does it bring? What impact, except for the incredibly big patch to specs (for every projects)? |
Even though I agree that def foo(something)
end
foo File.open("foo.cr") do
end In Ruby it works, but in Crystal you get an error:
|
I fail to find some article/text explaining the motivation behind the different precedence of
Having the option allows the programmer to choose which one to use and avoid parenthesis. In situations where both options ( I get that the ambiguity can be a bit disrupting but I think is a case where it is a language feature that might not be that straight forward to grasp even if it is used a lot. I think that been able to avoiding parenthesis in method calls is a key aspect for cleaner code. So I found it really hard to approve any change that goes in direction of mandatory parenthesis in the code. (Yet, I always liked them in method definition). As @asterite point out, the compiler helps a bit to discover wrong invocation earlier. |
First of all, I agree this is a bad implementation of disambiguating block syntax, because it ruins DSLs such as Being able to merge #4517 is the main motivation for merging this, I don't see any sane way to implement #4517 without making the two block syntaxes entirely interchangeable. And I think merging #4517 or similar has huge benefits (see #4504 for arguments). This PR was my first attempt at achieving this (I learnt a lot about the parser at least). I probably shouldn't have submitted it (it was 1AM and I didn't think much further than PR then sleep), and I should have thought about a better way to do this change some more. I think a better approach would be to change Thoughts? |
I'm thinking that maybe we can implement #4517 by being careful about the last argument to a call with a block. If the call has another call as its last argument, then # Can be changed, `map` doesn't have arguments
[1, 2, 3].map { |x|
}
def method
end
# Can't be changed: last argument is a method,
# so changing '{' to 'do' would associated it to 'foo'
foo method {
}
# Can be changed
foo method, 1 {
} Actually, after writing all of this, maybe we can make parentheses mandatory for calls that have a call with a # Error: `method { }` is an argument to 'foo`, so `foo` must use parentheses
foo method {
}
# OK
foo(method {
}) I actually find the second one less confusing. Or, well, I think just implementing #4517 with the "last argument is a call" in mind would be good-enough already. |
@asterite I thought up of a variation on your second idea (which replaces this PR) but I thought it might be a bit hard to do. I don't like the making the formatter format differently on fairly arbitrary rules. It makes the formatter unpredictable and annoying. |
This eliminates the subtle difference in associativity of
do/end
and{/}
by enforcing calls which pass a block to have parentheses around their arguments to disambiguate. This is first done by changing the formatter to enforce this rule, formatting, then changing the compiler.We should land the compiler change commit in
next_release + 1
to give people a formatter tool which they can use to get their projects up to date, but it's included in this PR for criticism.The largest hit is the spec DSL, every
it "foo" do
gets turned intoit("foo") do
, which is definitely uglier, but not impossible to live with I think. We could possibly also introduce a rule that this rule doesn't have to be followed if there's no block inside the block arguments, or we could unify both syntaxes to act likedo/end
, or we could leave this as-is (but I think the current state is confusing). So please start the discussion below.