Bug #11156
closedIndeterminate Behavior for Curly-braced Blocks with Function Argument Missing Parenthesis
Description
Given a function that takes an argument and a block, the behavior wildly varies when parenthesis are omitted from the functions argument.
Consider the following function:
require 'time'
def example(arg)
puts arg
if block_given?
puts yield
else
puts "no block"
end
end
Each of the following example sets presents one example
call with parentheses explicitly defined and one whether the parenthesis are omitted.
For most literals (e.g. 1, 'string', true, false, nil, %w[array]), missing parenthesis causes a syntax error.
example(1) { "block" }
# 1
# block
example 1 { "block" }
# SyntaxError: syntax error, unexpected '{', expecting end-of-input
Array literals, however, succeed:
example([1]) { "block" }
# 1
# block
example [1] { "block" }
# 1
# block
Identifiers are called as methods if parentheses are omitted:
example(Time) { "block "}
# Time
# block
example Time { "block" }
# NoMethodError: undefined method `Time' for main:Object
a = 1
example(a) { "block" }
# 1
# block
example a { "block" }
# NoMethodError: undefined method `a' for main:Object
Object with method calls simply skip the block when no parenthesis are present:
example(Time.now) { "block" }
# 2015-05-15 18:16:50 -0400
# block
example Time.now { "block" }
# 2015-05-15 18:16:50 -0400
# no block
Method calls with arguments behave about the same as the above...
example(Integer(1)) { "block" }
# 1
# block
example Integer(1) { "block" }
# 1
# no block
...except Time.parse
gets weird:
example(Time.parse('2015-01-01 0:00:00+00:00')) { "block" }
# 2015-01-01 00:00:00 +0000
# block
# => nil
example Time.parse('2015-01-01 0:00:00+00:00') { "block" }
# 0000-01-01 00:00:00 +0000 <---- Year 2000?!
# no block
# => nil
The lack of consistency across these use cases is extremely confusing and misleading. I'm of the opinion that parentheses omission should, in all cases, lead to a syntax error.
Updated by nobu (Nobuyoshi Nakada) almost 9 years ago
- Description updated (diff)
- Status changed from Open to Rejected
What's your point?
Time.parse
states as:
# If a block is given, the year described in +date+ is converted by the
# block. For example:
#
# Time.parse(...) {|y| 0 <= y && y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y}
Updated by faraz (Faraz Yashar) almost 9 years ago
I'm trying to demonstrate that the syntax is very confusing and error-prone, especially given that do ... end
blocks do not behave similarly due to precedence differences.
Consider the travel_to
time travel method given by Rails' ActiveSupport::Testing::TimeHelpers
. One would naturally expect that the following two statements would behave equivalently...
travel_to Time.parse('2015-01-01 00:00:00 +0000') do
Time.now # => 2015-01-01 00:00:00 +0000
end
travel_to Time.parse('2014-01-01 00:00:00 +0000') {
Time.now # => 2015-05-16 03:52:24 -0400
}
...but they don't because of precedence behaviors. To me, these differences violate the principle of least astonishment, and they cause more confusion and open room for errors.