Project

General

Profile

Actions

Bug #20931

closed

Using `in` as an expression requires extra parentheses

Added by stephenprater (Stephen Prater) 14 days ago. Updated 14 days ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:120106]

Description

TBH - I'm not sure if this is a bug or not - but it certainly surprising behavior and I'd at least like to understand it.

Given a hash t - that can be pattern matched: t = {a: 1, b:1 }

r = t in {a: 1, c:1 } # returns `false`
r # {a: 1, c: 1} wat

Presumably this is because = binds higher than in - so that expression is equivalent to (r = t) in {a: 1, c: 1}

But in that case - why does using the results of in require an additional set of parentheses to avoid a syntax error when the result of the expression is used as an argument to a method?

puts(t in {a: 1, c: 1}) # syntax error
puts((t in {a: 1, c: 1}) # false

Especially since this works fine:

puts(case t; in { a: 1, c:1 }; true; else false; end)

Updated by alanwu (Alan Wu) 14 days ago · Edited

  • Status changed from Open to Rejected

I'm closing this since I'm pretty sure this isn't a bug. An imperfect explanation follows. Feel free to jump in if anyone has a better explanation.

To understand the precedence, note that in has a symbolic friend =>, and much like how or binds lower than ||, in binds lower than =>. (Runtime behavior of => and in are different, though.)

As for why it requires parentheses in argument context, it's consistent with other single word English keywords such as and, or, if, and unless:

$ for keyword in and or if unless; do ruby -vc -e "puts(1 $keyword 1)"; done       
ruby 3.4.0dev (2024-12-04T21:26:31Z master c0e12bf8d2) +PRISM [arm64-darwin24]
ruby: -e:1: syntax errors found (SyntaxError)
> 1 | puts(1 and 1)
    |        ^~~ unexpected 'and'; expected a `)` to close the arguments
    |             ^ unexpected ')', ignoring it
    |             ^ unexpected ')', expecting end-of-input
  2 | 
ruby 3.4.0dev (2024-12-04T21:26:31Z master c0e12bf8d2) +PRISM [arm64-darwin24]
ruby: -e:1: syntax errors found (SyntaxError)
> 1 | puts(1 or 1)
    |        ^~ unexpected 'or'; expected a `)` to close the arguments
    |            ^ unexpected ')', ignoring it
    |            ^ unexpected ')', expecting end-of-input
  2 | 
ruby 3.4.0dev (2024-12-04T21:26:31Z master c0e12bf8d2) +PRISM [arm64-darwin24]
-e:1: warning: literal in condition
ruby: -e:1: syntax errors found (SyntaxError)
> 1 | puts(1 if 1)
    |        ^~ unexpected 'if'; expected a `)` to close the arguments
    |            ^ unexpected ')', ignoring it
    |            ^ unexpected ')', expecting end-of-input
  2 | 
ruby 3.4.0dev (2024-12-04T21:26:31Z master c0e12bf8d2) +PRISM [arm64-darwin24]
-e:1: warning: literal in condition
ruby: -e:1: syntax errors found (SyntaxError)
> 1 | puts(1 unless 1)
    |        ^~~~~~ unexpected 'unless'; expected a `)` to close the arguments
    |                ^ unexpected ')', ignoring it
    |                ^ unexpected ')', expecting end-of-input
  2 | 

Allowing these limited examples to work as expected probably causes parsing ambiguity in some other cases, so they're rejected. But I'm no parser expert.

Updated by stephenprater (Stephen Prater) 14 days ago

That works for me - thanks for the explanation.

Updated by mame (Yusuke Endoh) 14 days ago · Edited

As for the limitation of in, there is a more easy-to-understand explanation. Consider foo(a in 1, 2, 3). This is very ambiguous because there are three possible interpretations: foo((a in 1), 2, 3), foo((a in 1, 2), 3), and foo((a in 1, 2, 3)). Note that a in 1, 2, 3 returns true when a = [1, 2, 3]. So parentheses are necessary.

I understand that it is confusing to need double parentheses when you use in as a simple expression without following commas. But no good solution came to mind.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like1