Forum: Ruby Method named ***(other)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
3327593586837f472a611fc08836506b?d=identicon&s=25 Julien Gaugaz (Guest)
on 2006-12-21 17:08
(Received via mailing list)
Hi!

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

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 ;-) )

Thanks a lot in advance for your help and advices!

Julien
Caf3d97ceff60d6d105c45305d34658c?d=identicon&s=25 Vidar Hokstad (Guest)
on 2006-12-21 17:30
(Received via mailing list)
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 :(

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
3327593586837f472a611fc08836506b?d=identicon&s=25 Julien Gaugaz (Guest)
on 2006-12-21 17:46
(Received via mailing list)
> 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
25e11a00a89683f7e01e425a1a6e305c?d=identicon&s=25 Wilson Bilkovich (Guest)
on 2006-12-21 17:52
(Received via mailing list)
On 12/21/06, Julien Gaugaz <gaugaz@l3s.de> wrote:
> > 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.
Also, *** is unreadably nasty, so it's good that you can't name a
method that. :)
3327593586837f472a611fc08836506b?d=identicon&s=25 Julien Gaugaz (Guest)
on 2006-12-21 18:06
(Received via mailing list)
>> > whereas the infix operators that exist in Ruby that you can redefine
> 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!!!! :-D Thanks a lot!
> Also, *** is unreadably nasty, so it's good that you can't name a
> method that. :)
:-)
I found it ok compared to ===. But if the interpreter can't read it...
Caf3d97ceff60d6d105c45305d34658c?d=identicon&s=25 Vidar Hokstad (Guest)
on 2006-12-21 18:10
(Received via mailing list)
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
3327593586837f472a611fc08836506b?d=identicon&s=25 Julien Gaugaz (Guest)
on 2006-12-21 18:29
(Received via mailing list)
Vidar Hokstad wrote:
> Again, the only reason you can use a name like '**' is that it's one of
>
> 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 ;-)

Thank you again for those enlightenments!

Cheers,

Julien
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Trans (Guest)
on 2006-12-21 19:51
(Received via mailing list)
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.
7aa3c32f034122ff929be64f5de237e7?d=identicon&s=25 Luciano Ramalho (Guest)
on 2006-12-21 20:01
(Received via mailing list)
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
83ca41657a99b65d99889abe712ba5e2?d=identicon&s=25 Jason Roelofs (Guest)
on 2006-12-21 20:07
(Received via mailing list)
On 12/21/06, Luciano Ramalho <ramalho@gmail.com> wrote:
> Cheers,
>
> Luciano
>
>

"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
7aa3c32f034122ff929be64f5de237e7?d=identicon&s=25 Luciano Ramalho (Guest)
on 2006-12-21 20:39
(Received via mailing list)
> "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
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Trans (Guest)
on 2006-12-21 23:05
(Received via mailing list)
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.
7aa3c32f034122ff929be64f5de237e7?d=identicon&s=25 Luciano Ramalho (Guest)
on 2006-12-21 23:11
(Received via mailing list)
Thanks, T. That was awesome!

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

Cheers,

Luciano
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Trans (Guest)
on 2006-12-22 01:44
(Received via mailing list)
Luciano Ramalho wrote:
> > More or less.
> >   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.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-12-23 19:03
(Received via mailing list)
On Fri, Dec 22, 2006 at 03:49:30AM +0900, Trans wrote:
>
> 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 ;)
This topic is locked and can not be replied to.