Feature #17398
closedSyntaxError in endless method
Description
This works:
def foo() = puts("bar")
This does not:
def foo() = puts "bar"
# ^ syntax error, unexpected string literal, expecting `do' or '{' or '('
Is this intentional or accidental? Not sure how it is reasoned.
Files
Updated by mame (Yusuke Endoh) over 4 years ago
The body of an endless method must be an expression (called "arg" in the syntax rules of parse.y). puts("bar")
is an expression, but puts "bar"
is a statement (called "command" in the syntax rules).
I think it could be a bit confusing, but I have no idea whether we can/should allow a statement as a body of an endless method.
Updated by zverok (Victor Shepelev) over 4 years ago
@mame (Yusuke Endoh) Hmm, haven't thought about it from this perspective... Can you please explain a bit? As far as I can see, in, say, assignment context it behaves like an expression:
result = puts 'foo'
# prints "foo", result = nil
I am just trying to describe the behavior in full for the next installment of my changelog and this aspect is quite confusing for me... Though, it is not endless-method specific, as far as I can see:
y = sin x # OK
y = 1 + sin x
# ^ unexpected local variable or method, expecting `do' or '{' or '('
What's the "rule of thumb" to understand this better?
Updated by Eregon (Benoit Daloze) over 4 years ago
Conceptually, according to the typical definition in computer science, both puts("bar")
and puts "bar"
are expressions (i.e., they return a value, and if it was some other method than puts
it would also not always be nil
).
It might be slightly less clear for e.g. a = 42
(it's still an expression, it still returns a value), but I think puts "bar"
is clear that it should be the same as puts("bar")
, except for precedence.
So it's probably going to be very difficult to explain the actual condition, other than showing specific examples.
Updated by austin (Austin Ziegler) over 4 years ago
Eregon (Benoit Daloze) wrote in #note-3:
So it's probably going to be very difficult to explain the actual condition, other than showing specific examples.
Endless methods definitions don’t support poetry mode?
Updated by mame (Yusuke Endoh) over 4 years ago
The following patch allows def foo() = puts "bar"
. It brings no parser conflict.
https://gist.github.com/mame/0773bf3938e046e2b608de5fb2a826c8
However, it is not perfect. private def foo() = puts "foo"
does not parse.
private var = puts "bar"
is not allowed neither, so I have no idea how to allow this.
Updated by matz (Yukihiro Matsumoto) over 4 years ago
- Tracker changed from Bug to Feature
- Backport deleted (
2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN)
Pros
- More intuitive / consistent / natural
Cons
- Duplicated syntax rules
- Even more complex syntax
If I were young, I would add @mame's patch. I did similar decisions many times in the past. But Ruby has been mature and complex, I now feel reluctant. Let us consider this idea for a while.
Matz.
Updated by mame (Yusuke Endoh) over 4 years ago
- File allow-command-style-endless-method-def.patch allow-command-style-endless-method-def.patch added
I'm attaching an updated patch with a test.
Updated by matz (Yukihiro Matsumoto) over 4 years ago
I have considered this issue for a while and concluded it should be merged to be consistent with the assignment statement.
Matz.
Updated by mame (Yusuke Endoh) over 4 years ago
- Status changed from Open to Closed
Applied in changeset git|31794d2e733e081e4e221f27aff6380393981681.
parse.y: Allow "command" syntax in endless method definition
This change allows def hello = puts "Hello"
without parentheses.
Note that private def hello = puts "Hello"
does not parse for
technical reason.
[Feature #17398]
Updated by yui-knk (Kaneko Yuichiro) 15 days ago
Note that private def hello = puts "Hello" does not parse for technical reason.
It's possible.
Patch is https://github.com/yui-knk/ruby/tree/bug_17398.
This change allows single endless method definition with command body as an argument of the method.
Some cases you might be aware are
-
private :m, def hello = puts "Hello"
- This is SyntaxError because only single argument case is allowed.
- This is same with one command is passed to argument.
obj.m 0, cmd 1, 2
is SyntaxError.
-
private def hello = puts "Hello", "World"
- This is interpreted as
private def hello = puts("Hello", "World")
notprivate (def hello = puts "Hello"), "World"
. - This is same with one command is passed to argument.
obj.m cmd 1, 2
is interpreted asobj.m cmd(1, 2)
notobj.m (cmd 1), 2
.
- This is interpreted as
-
private def hello = puts "Hello" do expr end
- This is interpreted as
private (def hello = puts "Hello") do expr end
notprivate (def hello = puts "Hello" do expr end)
- This is same with one command is passed to argument.
obj.m cmd 1, 2 do expr end
is interpreted asobj.m (cmd 1, 2) do expr end
notobj.m (cmd 1, 2 do expr end)
- This is interpreted as
Updated by mame (Yusuke Endoh) 15 days ago
- Status changed from Closed to Open
Thanks, it looks good. @matz (Yukihiro Matsumoto) what do you think?
Updated by matz (Yukihiro Matsumoto) 14 days ago
Sounds reasonable, considering existing grammar.
Matz.
Updated by mame (Yusuke Endoh) 14 days ago
- Status changed from Open to Assigned
- Assignee set to prism
Ok, then we need a patch for prism
Updated by Earlopain (Earlopain _) 14 days ago
It is easy for prism to accept this as well: https://github.com/ruby/prism/pull/3632
I checked the above examples and prism seems to interpret them in the same way.
Updated by Earlopain (Earlopain _) 3 days ago
- Status changed from Assigned to Closed
Applied in changeset git|f2dbc4ec82a0e103ac1e3f64f5983540cdc75fd3.
[ruby/prism] [Bug #17398] Allow private def hello = puts "Hello"
This was a limitation of parse.y that prism intentionally replicated.
Updated by kddnewton (Kevin Newton) 3 days ago
- Status changed from Closed to Open
- Assignee deleted (
prism)
This is now fixed in prism, will need a fix for parse.y.
Updated by yui-knk (Kaneko Yuichiro) 3 days ago
- Status changed from Open to Closed
Applied in changeset git|a8c829e7f0dc20c3213d23fb9722fabc74fe1a9d.
[Bug #17398] Allow private def hello = puts "Hello"