Project

General

Profile

Actions

Feature #3845

closed

"in" infix operator

Added by mame (Yusuke Endoh) over 13 years ago. Updated about 12 years ago.

Status:
Rejected
Target version:
-
[ruby-core:32454]

Description

=begin
Hi,

I'd propose "in" infix operator.

( in ) yields true when is included in .
Otherwise it yields false.

p "found" if 1 in 1, 2, 3 #=> found
p "not found" if 0 in 1, 2, 3 #=> not found

"in" operator is clearer to the reader than Array#include?:

p "found" if [1, 2, 3].include?(1)
p "not found" if [1, 2, 3].include?(0)

This proposal is similar to Object#in? proposed in [ruby-core:23543].
But there are two differences:

  • "in" operator does not pollute name space of Object class

  • each candidate of "in" is evaluated lazily; for example,

    1 in 1, 2, foo()

    does not call the method "foo" because 1 is found before that.

Note that this proposal ensures the syntax compatibility, since
"in" is already a keyword for "for" statement. But "for" statement
is rarely used. This proposal utilizes the rarely-used keyword.

I wrote an experimental patch. It implements the operator as a
syntactic sugar to "case" statement:

in
=> (case ; when ; true; else false; end)

The patch causes no parser conflict.

One more thing. The following expression is rejected:

foo(x in 1, 2, 3)

This is because it is ambiguous; this expression can be interpreted
as three ways:

foo((x in 1), 2, 3)
foo((x in 1, 2), 3)
foo((x in 1, 2, 3))

You need write parentheses explicitly.

What do you think?

diff --git a/parse.y b/parse.y
index e085088..64318bd 100644
--- a/parse.y
+++ b/parse.y
@@ -745,6 +745,7 @@ static void token_info_pop(struct parser_params*, const char token);
%nonassoc modifier_if modifier_unless modifier_while modifier_until
%left keyword_or keyword_and
%right keyword_not
+%nonassoc keyword_in
%nonassoc keyword_defined
%right '=' tOP_ASGN
%left modifier_rescue
@@ -1258,6 +1259,14 @@ expr : command_call
$$ = dispatch2(unary, ripper_id2sym('!'), $2);
%
/
}

  •  | expr keyword_in args
    
  •      {
    
  •      /*%%%*/
    
  •  	$$ = NEW_CASE($1, NEW_WHEN($3, NEW_TRUE(), NEW_FALSE()));
    
  •      /*%
    
  •  	$$ = dispatch2(in, $1, $3);
    
  •      %*/
    
  •      }
     | arg
     ;
    

diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index 5d76941..6005457 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -1107,4 +1107,8 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
parse('/', :compile_error) {|msg| compile_error = msg}
assert_equal("unterminated regexp meets end of file", compile_error)
end
+

  • def test_in
  • assert_equal("[in(1,[1,2,3])]", parse('1 in 1, 2, 3'))
  • end
    end if ripper_test

--
Yusuke Endoh
=end


Files

in.expression.diff (698 Bytes) in.expression.diff adgar (Michael Edgar), 07/10/2011 09:22 AM

Related issues 1 (0 open1 closed)

Has duplicate Ruby master - Feature #4402: Include an "in" operatorClosed02/16/2011Actions
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0