Project

General

Profile

Bug #11156

Indeterminate Behavior for Curly-braced Blocks with Function Argument Missing Parenthesis

Added by faraz (Faraz Yashar) over 5 years ago. Updated over 5 years ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
[ruby-core:<unknown>]

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.

#1

Updated by nobu (Nobuyoshi Nakada) over 5 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}
#2

Updated by faraz (Faraz Yashar) over 5 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.

Also available in: Atom PDF