Project

General

Profile

Feature #2013

Updated by mame (Yusuke Endoh) over 11 years ago

=begin 
  
  This makes the * operator operate more similarly to the + and - operators.    The binary versions of + and - call methods named + and -, while the unary versions call methods named +@ and -@.    The binary * operator calls the method named *, but the unary * operator calls the method to_a currently, and used to call to_ary in 1.8.6 (and at some point may have called to_splat). 
 
  I think this makes for more consistent behavior, and hopefully it isn't just a foolish consistency.    I brought up this idea as a question in Shugo Maeda's presentation at RubyKaigi 2009, and discussed it with Matz at Lone Star Ruby Conf 2009, and he thought the idea had merit. 
 
  Here's a basic example how this would look: 
 
    class MultiplePersonality 
      def *@ 
        [self, [self, self]] 
      end 
    end 
 
    mp = MultiplePersonality.new 
    p1, mp2 = *mp 
 
  This comes with a patch that appears to work from my simple testing, but this is my first time working with the internal ruby code, so I apologize in advance if it doesn't do things correctly. 
 
  The patch modifies parse.y so the above is no longer a syntax error.    It adds a USTAR token to the parser to represent that *@ token.    It modifies the splatting to call *@ instead of to_a.    Also, for backwards compatibility, it adds *@ to BasicObject, and has it call to_a if it responds to to_a.    This allows code that defines to_a and expects that the unary * operator will call to_a to still work.   
 
  This patch is mostly for consistency, but it also allows the programmer to make to_a return one thing, and the unary * operator return something else.    I can think of the following use case: 
 
    # Represents an abstract set of rows in the database 
    class Dataset 
      def to_a 
        retrieve_database_rows 
      end 
      def *@ 
        [self] 
      end 
    end 
    dataset = Dataset.new 
 
    # Explicitly asking for an array means I want 
    # an array of database rows represented by the 
    # dataset. 
    rows = dataset.to_a 
 
    # If the dataset had any rows, I want to debug print 
    # each row separately.    However, if it did not have 
    # any rows, I want to debug print the dataset itself. 
    p(*(rows.empty? ? dataset : rows)) 
 
  I'd like to thank Eleanor McHugh for helping me find the key part of ruby that needed to be modified to support this (in vm_insnhelper.c). 
 
 =end 
 

Back