Gangmax Blog

自由之思想,独立之精神

Ruby学习笔记第五篇: Method Call

| Comments

The following code shows that: in Ruby, a method and a variable can share the same name in the same scope. If doing so, the Ruby intepreter will parse the name as variable, unless some explict method call sign is following:

  1. Parenthesis(“()”);

  2. Method call argument(s).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
~$ irb
1.9.2p290 :001 > def my_m1
1.9.2p290 :002?>   puts "hello from my_m1"
1.9.2p290 :003?>   end
 => nil
1.9.2p290 :004 > my_m1 = 1
 => 1
1.9.2p290 :005 > my_m1
 => 1
1.9.2p290 :006 > my_m1()
hello from my_m1
 => nil
1.9.2p290 :007 > def MyM2
1.9.2p290 :008?>   puts "hello from MyM2"
1.9.2p290 :009?>   end
 => nil
1.9.2p290 :010 > MyM2 = 2
 => 2
1.9.2p290 :011 > puts MyM2
2
 => nil
1.9.2p290 :012 > MyM2()
hello from MyM2
 => nil
1.9.2p290 :013 > def my_m3(a)
1.9.2p290 :014?>   puts "hello from my_m3(#{a})!"
1.9.2p290 :015?>   end
 => nil
1.9.2p290 :016 > my_m3 = 3
 => 3
1.9.2p290 :017 > my_m3
 => 3
1.9.2p290 :018 > my_m3 3
hello from my_m3(3)!
 => nil
1.9.2p290 :019 > my_m3(3)
hello from my_m3(3)!
 => nil
1.9.2p290 :020 > my_m3 3
hello from my_m3(3)!
 => nil
1.9.2p290 :021 >

In Ruby code, you can see some ODD methods like Array’s “[]=” and “<<”. which you don’t have to use the “dot” manner to call, but an operator way instead:

1
2
3
a = [1, 2, 3]
a << 4        # Return "[1, 2, 3, 4]".
a[0] = 3  # Shorthand for a.[]=(0, 3) and will return "[3, 2, 3, 4]".

But for a normal method, you cannot call it in the operator way. This confuse me. What kind of methods can be called as the operator way, why the other methods cannot?

I found the answer from here and here by ”Alberto Moriconi” and ”Jörg W Mittag”:

From “Alberto Moriconi”:

Ruby also allows to define operators using the operator symbol as the method name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
╔═══════════════════════════╦═════════════════════════════════════════════╦═══════╗
 Operators (by precedence)                  Operations                   Arity 
╠═══════════════════════════╬═════════════════════════════════════════════╬═══════╣
 ! ~ +                      Boolean NOT, bitwise complement, unary plus      1 
                            (define with method name +@, Ruby 1.9+)            
                                                                               
 **                         Exponentiation                                   2 
                                                                               
 -                          Unary minus (define with method name -@)         1 
                                                                               
 * / %                      Multiplication, division, modulo                 2 
                                                                               
 + -                        Addition, subtraction                            2 
                                                                               
 << >>                      Bitwise shift                                    2 
                                                                               
 &                          Bitwise AND                                      2 
                                                                               
 | ^                        Bitwise OR, Bitwise XOR                          2 
                                                                               
 < <= => >                  Ordering                                         2 
                                                                               
 == === != =~ !~ <=>        Equality, pattern matching, comparison           2 
╚═══════════════════════════╩═════════════════════════════════════════════╩═══════╝

Unary operator methods are passed no arguments; binary operator methods are passed an argument, and operate on it and on self.

It’s important to adhere strictly to the arity of the operators; while it is possible to define operator methods with a different arity (e.g. a + method that takes two arguments), Ruby would not allow you to call the method with operator syntax (it would however work with dot syntax).

It’s good practice to adhere to the original semantics of the operators as much as possible: it should be intuitive to someone who knows the original meaning of the operator how it works with user defined classes.

The language also offers syntactic sugar for the special, non-operator ,[] method that is normally used for accessing array and hash values. The [] method can be defined with arbitrary arity.

For every binary operator in the table, except ordering, equality, comparison and pattern matching, Ruby also offers shorthand for abbreviated assignment (e.g. x += y expands to x = x + y); you can’t define them as methods, but you can alter their behavior defining the operators on which they’re based.

From “Jörg W Mittag”:

There is no particular reason why Ruby couldn’t support user-defined operators in a style similar to Scala. There is a reason why Ruby can’t support arbitrary methods in operator position, simply because

1
foo plus bar

is already legal, and thus this would be a backwards-incompatible change.

Another thing to consider is that Ruby wasn’t actually fully designed in advance. It was designed through its implementation. Which means that in a lot of places, the implementation is leaking through. For example, there is absolutely no logical reason why

1
puts(!true)

is legal but

1
puts(not true)

isn’t. The only reason why this is so, is because Matz used an LALR(1) parser to parse a non-LALR(1) language. If he had designed the language first, he would have never picked an LALR(1) parser in the first place, and the expression would be legal.

The Refinement feature currently being discussed on ruby-core is another example. The way it is currently specified, will make it impossible to optimize method calls and inline methods, even if the program in question doesn’t actually use Refinements at all. With just a simple tweak, it can be just as expressive and powerful, and ensure that the pessimization cost is only incurred for scopes that actually use Refinements. Apparently, the sole reason why it was specified this way, is that a) it was easier to prototype this way, and b) YARV doesn’t have an optimizer, so nobody even bothered to think about the implications (well, nobody except Charles Oliver Nutter).

So, for basically any question you have about Ruby’s design, the answer will almost always be either “because Matz said so” or “because in 1993 it was easier to implement that way”.

Comments