Method named ***(other)

Hi!

It's several hours now that i'm trying to find out what's special with *** for a method name, without success :frowning:

I'm trying to add an element wise multiplication method to the Matrix class. For this I created a collect2 method for Matrix similar to the one in Vector as follows:

class Matrix
  def collect2(other)
    result = self.clone
    (0...self.row_size).each {|rowi|
      (0...self.column_size).each {|columni|
        result[rowi,columni] = yield (self[rowi,columni], other[rowi,columni])
      }
    }
    result
  end
  def []=(i,j,v)
      @rows[i][j] = v
   end
end

And i would like now to add a method *** to Matrix such that we could call matrix1 *** matrix2. Like:
m = Matrix[[1,2,3],[-1,-2,-3], [4,5,6]]
n = Matrix[[1,2,3],[7,8,9], [-1,-2,-3]]
m *** n
=> Matrix[[1, 4, 9], [-7, -16, -27], [-4, -10, -18]]

Now if I define this method as follows:
  def ***(other)
    collect2(other) do |e1,e2|
      if(e1 and e2)
        e1*e2
      end
    end
  end

I get the following error:
SyntaxError: compile error
(irb):550: parse error, unexpected tLPAREN, expecting '\n' or ';'
  def ***(other)
          ^
(irb):557: parse error, unexpected kEND, expecting $
    from (irb):557
    from :0

The same with def *** other works (I can define it without error). But now a call to m *** n gives me:
SyntaxError: compile error
(irb):33: parse error, unexpected tSTAR
m *** n
     ^
    from (irb):33

···

from :0

And m.*** n (notice the period after m) gives:
TypeError: Array can't be coerced into Fixnum
    from (irb):8:in `*'
    from (irb):8:in `**'
    from (irb):24:in `collect2'
    from (irb):23:in `each'
    from (irb):23:in `collect2'
    from (irb):22:in `each'
    from (irb):22:in `collect2'
    from (irb):6:in `**'
    from (irb):35
    from :0

Whereas if I name the method with two stars only, i.e. def **(other), it can be defined, and then m ** n gives the expected result.

I therefore have the following questions:
- Why is it possible to define def *** other but not def ***(other) ?
- What is the difference between m.*** n and m *** n (with and without period after m) ?
- Why do I get an error with m.*** n (with period) but not with m ** n ?

I would greatly appreciate any answer of pointers to some information sources (I tried but it is not easy to search for *** in a web search engine :wink: )

Thanks a lot in advance for your help and advices!

Julien

Julien Gaugaz wrote:

Hi!

It's several hours now that i'm trying to find out what's special with
*** for a method name, without success :frowning:

That should be your clue. There _isn't_ anything special about "***",
whereas the infix operators that exist in Ruby that you can redefine
require special treatment by the interpreter. You can't expand the set
of operators, and '***' isn't among them.

Vidar

Julien Gaugaz wrote:

Hi!

It's several hours now that i'm trying to find out what's special with
*** for a method name, without success :frowning:

That should be your clue. There _isn't_ anything special about "***",
whereas the infix operators that exist in Ruby that you can redefine
require special treatment by the interpreter. You can't expand the set
of operators, and '***' isn't among them.

Ah, ok, thanks a lot. I understood why i could use '***' as an infix operator. But still, there shouldn't be any difference between a call like m.*** n and m.triple_star n... But it seems there is since m.triple_star works and m.*** don't.

I don't get it.

Julien

···

Vidar

Thanks for the reply, Vidar.

Is there a way I can ask irb to list all the operators which can be overloaded?

Cheers,

Luciano

···

On 12/21/06, Vidar Hokstad <vidar.hokstad@gmail.com> wrote:

You can't expand the set
of operators, and '***' isn't among them.

The basic problem is that ** is right-associative, and * is
left-associative. The existing grammar can't resolve the apparent
conflict.
Also, *** is unreadably nasty, so it's good that you can't name a
method that. :slight_smile:

···

On 12/21/06, Julien Gaugaz <gaugaz@l3s.de> wrote:

>
> Julien Gaugaz wrote:
>> Hi!
>>
>> It's several hours now that i'm trying to find out what's special with
>> *** for a method name, without success :frowning:
>
> That should be your clue. There _isn't_ anything special about "***",
> whereas the infix operators that exist in Ruby that you can redefine
> require special treatment by the interpreter. You can't expand the set
> of operators, and '***' isn't among them.
Ah, ok, thanks a lot. I understood why i could use '***' as an infix
operator. But still, there shouldn't be any difference between a call
like m.*** n and m.triple_star n... But it seems there is since
m.triple_star works and m.*** don't.

I don't get it.

Julien Gaugaz wrote:

Ah, ok, thanks a lot. I understood why i could use '***' as an infix
operator. But still, there shouldn't be any difference between a call
like m.*** n and m.triple_star n... But it seems there is since
m.triple_star works and m.*** don't.

I don't get it.

Again, the only reason you can use a name like '**' is that it's one of
the predefined operators. You can't just use any characters you want in
a method name - the only method names you can use "special" characters
like '*' in are method names matching the specific operators you can
override.

I think your confusion probably stems from the fact that this parses
without errors:

  def *** other
  end

Hower, try to run this:

  class Foo
      def *** other
      end
  end

  p Foo.new.methods.sort

This should print out a sorted list of methods of instances of Foo.
You'll see "**" shows up as the first method. The reason is that "def
*** other" gets parsed as "def ** *other" (note the space). "*" in
this case is the "splat" operator, that indicates that "other" will
hold an arbitrary number of arguments in an array.

So you haven't defined Foo#***, but Foo#**.

Vidar

Vidar

"operators" are only special in that the parser is built to recognize a few
constructs, mainly the mathematical operators (+, -, /, *, etc). For the
programmer's case, they are all methods on some object, and all methods can
be overwritten. So

class A
  def +(args)
  end
end

class B
  def *(args)
  end
end

def /(args)
end

etc...

Always keep in mind this simple feature: 1 + 1 => 1.+(1) and you'll realize
that there really isn't "operator overloading" in Ruby, everything is just a
message.

Jason

···

On 12/21/06, Luciano Ramalho <ramalho@gmail.com> wrote:

On 12/21/06, Vidar Hokstad <vidar.hokstad@gmail.com> wrote:
> You can't expand the set
> of operators, and '***' isn't among them.

Thanks for the reply, Vidar.

Is there a way I can ask irb to list all the operators which can be
overloaded?

Cheers,

Luciano

>
> Julien Gaugaz wrote:
>> Hi!
>>
>> It's several hours now that i'm trying to find out what's special with
>> *** for a method name, without success :frowning:
>
> That should be your clue. There _isn't_ anything special about "***",
> whereas the infix operators that exist in Ruby that you can redefine
> require special treatment by the interpreter. You can't expand the set
> of operators, and '***' isn't among them.
Ah, ok, thanks a lot. I understood why i could use '***' as an infix
operator. But still, there shouldn't be any difference between a call
like m.*** n and m.triple_star n... But it seems there is since
m.triple_star works and m.*** don't.

I don't get it.

The basic problem is that ** is right-associative, and * is
left-associative. The existing grammar can't resolve the apparent
conflict.

Ah ok! I get it!!!! :smiley: Thanks a lot!

Also, *** is unreadably nasty, so it's good that you can't name a
method that. :slight_smile:

:slight_smile:
I found it ok compared to ===. But if the interpreter can't read it...

···

On 12/21/06, Julien Gaugaz <gaugaz@l3s.de> wrote:

Vidar Hokstad wrote:

Julien Gaugaz wrote:
  

Ah, ok, thanks a lot. I understood why i could use '***' as an infix
operator. But still, there shouldn't be any difference between a call
like m.*** n and m.triple_star n... But it seems there is since
m.triple_star works and m.*** don't.

I don't get it.
    
Again, the only reason you can use a name like '**' is that it's one of
the predefined operators. You can't just use any characters you want in
a method name - the only method names you can use "special" characters
like '*' in are method names matching the specific operators you can
override.

I think your confusion probably stems from the fact that this parses
without errors:

  def *** other
  end

Hower, try to run this:

  class Foo
      def *** other
      end
  end

  p Foo.new.methods.sort

This should print out a sorted list of methods of instances of Foo.
You'll see "**" shows up as the first method. The reason is that "def
*** other" gets parsed as "def ** *other" (note the space). "*" in
this case is the "splat" operator, that indicates that "other" will
hold an arbitrary number of arguments in an array.

So you haven't defined Foo#***, but Foo#**.
  

Nice explanation! I was indeed misled by the fact that some operators are actually implemented as methods. And therefore considered the character '*' as a standard one for a method...

As you might have guessed I'm a complete newbie to Ruby :wink:

Thank you again for those enlightenments!

Cheers,

Julien

···

Vidar

Vidar

"operators" are only special in that the parser is built to recognize a few
constructs, mainly the mathematical operators (+, -, /, *, etc). For the
programmer's case, they are all methods on some object, and all methods can
be overwritten. [...]

Thanks for the reply, Jason. I realise +, * etc. are method names, but
as Julien just discovered, not all character sequences may be used as
method names.

There is a table in the Pickaxe book [1][2] which lists all Ruby
operators, noting which of them are defined as methods and thus can be
overridden.

I was just wondering whether there is a way, via introspection, to
fetch that list programmatically from Ruby, short of inspecting the
source code for the interpreter.

Cheers,

Luciano

[1] page 339 in 2nd ed
[2] http://www.rubycentral.com/book/html/language.html

Julien Gaugaz wrote:

I found it ok compared to ===. But if the interpreter can't read it...

That one's ugly too -- a word alias for it is in my bag of wishes.

T.

Luciano Ramalho wrote:

I was just wondering whether there is a way, via introspection, to
fetch that list programmatically from Ruby, short of inspecting the
source code for the interpreter.

More or less.

  h =
  ObjectSpace.each_object(Class){|c|
    h |= c.public_instance_methods.select{|m| m =~ /^\W/}
    h |= c.private_instance_methods.select{|m| m =~ /^\W/}
  }
  ObjectSpace.each_object(Module){|c|
    h |= c.public_instance_methods.select{|m| m =~ /^\W/}
    h |= c.private_instance_methods.select{|m| m =~ /^\W/}
  }
  p h

There's probably some clever way to compute precedence too.

T.

Thanks, T. That was awesome!

By why did you say "more or less"? Do you think your code could be
missing something?

Cheers,

Luciano

···

On 12/21/06, Trans <transfire@gmail.com> wrote:

More or less.

  h =
  ObjectSpace.each_object(Class){|c|
    h |= c.public_instance_methods.select{|m| m =~ /^\W/}
    h |= c.private_instance_methods.select{|m| m =~ /^\W/}
  }
  ObjectSpace.each_object(Module){|c|
    h |= c.public_instance_methods.select{|m| m =~ /^\W/}
    h |= c.private_instance_methods.select{|m| m =~ /^\W/}
  }
  p h

There's probably some clever way to compute precedence too.

T.

Julien Gaugaz wrote:

> I found it ok compared to ===. But if the interpreter can't read it...

That one's ugly too -- a word alias for it is in my bag of wishes.

I'd say "casecmp" but that one's already taken :wink:

···

On Fri, Dec 22, 2006 at 03:49:30AM +0900, Trans wrote:

T.

Luciano Ramalho wrote:

Thanks, T. That was awesome!

By why did you say "more or less"? Do you think your code could be
missing something?

Cheers,

Luciano

> More or less.
>
> h =
> ObjectSpace.each_object(Class){|c|
> h |= c.public_instance_methods.select{|m| m =~ /^\W/}
> h |= c.private_instance_methods.select{|m| m =~ /^\W/}
> }
> ObjectSpace.each_object(Module){|c|
> h |= c.public_instance_methods.select{|m| m =~ /^\W/}
> h |= c.private_instance_methods.select{|m| m =~ /^\W/}
> }
> p h
>
> There's probably some clever way to compute precedence too.

+@ and -@ are prefix operators, so you may not want those. And and
= are a special in theie own way as well. So that's the possible
"more". As for "less", there may be an operator or two that is legal
but is not defined anywhere in the core set of classes/modules --though
unlikely, I did not check to make sure all where accounted.

T.

···

On 12/21/06, Trans <transfire@gmail.com> wrote: