From: Michael Edgar Date: 2011-07-10T09:22:43+09:00 Subject: [ruby-core:37941] [Ruby 1.9 - Feature #3845] "in" infix operator Issue #3845 has been updated by Michael Edgar. File in.expression.diff added I personally believe `in` belongs as an operator, it should match natural, mathematical, set-inclusion notation, and it should invoke `include?`. Many have discussed how it is just as possible to write "does S include x" as well as "is x in S": especially in English, there are many ways of writing things. As there are in Ruby! This should not distract us from why the idea has been proposed in the first place. In mathematics, we very rarely write "Set S includes x", let alone as a predicate. Instead, we write, (in LaTeX), x \in S. This is because most commonly the focus of this predicate is the element in question: is it in the set or not? This is a property of the set, but the focus of discourse is the (potential) element. The Ruby method belongs on the set, OO-speaking, because it is in charge of the information involved. But it is not unreasonable to note that writing S.includes?(x) introduces a mismatch between how computer scientists typically consider such questions. Ruby's OO syntax does not naturally allow us to express this "foo in S" idea, in that order, without adding a method to all potential elements. I don't believe introducing a new method, `.in?` is a good idea. There should be no reason to introduce a misleading method name that suggests an element might know what sets it is in. Instead, I think that introducing `in` as an syntactic construct (much like for loop syntax) is appropriate. Since Ruby already has an idiomatic inclusion method name, `include?`, I believe it should invoke that, right to left (`foo in bar` means `bar.include?(foo)`). Here, the parallel with for loop syntax becomes more clear. For loops address the same concern: it makes sense to write, in English, "array, each of your elements, x, should do this" just as it makes sense to say "for each x in the array, do this". OO-style invocation supports the former, but not the latter, and so we have a syntax which reverses the order: in for loops, the receiver comes after the variable names it uses. This supports a different, natural way to describe loops. Just like `in`, it works by using a convention-based method name. For loops in Ruby are maligned primarily due to potentially-surprising scoping issues, but their syntax itself is subjectively attractive. I have attached a patch which incorporates this approach. It includes the appropriate ripper event(s). ---------------------------------------- Feature #3845: "in" infix operator http://redmine.ruby-lang.org/issues/3845 Author: Yusuke Endoh Status: Assigned Priority: Normal Assignee: Yukihiro Matsumoto Category: Target version: =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 -- http://redmine.ruby-lang.org