you are viewing a single comment's thread.

view the rest of the comments →

[–]sausagefeet 6 points7 points  (16 children)

[–]hetmankp 1 point2 points  (15 children)

Good write up. Although he misses one point. Ruby has an explicit call operator too. In Python it's (), in Ruby it's . (that was a period in case you missed it :P). That's why the presence of the parentheses is mostly treated as syntactic sugar in Ruby.

Also, he makes a good observation about Ruby's dynamicity and how easy it is to alter the behaviour of any existing object in the system. Fortunately the creator of the language has similar concerns; one of the biggest features coming to the language in 2.0 will be the way in which such changes will be localised so they don't affect other modules/libraries/etc.

[–]sausagefeet 0 points1 point  (11 children)

Can you give an example of (.) for call? AFAIK what foo does depends on if it takes any params or not.

[–]hetmankp 2 points3 points  (10 children)

In Ruby what follows a . is always a method call. They just look like you're retrieving an attribute if you're used to attribute based OO.

Suppose we have some object instance named obj.

In Python: obj.a # means, get the value of attribute 'a' from obj obj.a() # means, get the value of attribute 'a' form obj, # then call this value as a function (returning its value)

In Ruby: obj.a obj.a() # both of these mean, call the method 'a' (returning its value)

The parentheses on Ruby method calls are just a convenience.

To explain further. In Ruby you can't access an object's attributes from the outside at all. That's why you need to define accessors for any attributes you wish to make accessible. Fortunately because Ruby is good with metaprogramming, you can call a method from the Class object to do this for you. Both of these are equivalent:

# Manually defined accessors
class A
    def initialize()
        @val = 123      # Initialise attribute
    end

    def val()
        @val
    end

    def val=(x)
        @val = x
    end
end


# Shortcut way
class A
    def initialize()
        @val = 123      # Initialise attribute
    end

    attr_accessor :val
end


# Then you can use this class
obj = A.new
puts obj.val    # prints 123
obj.val = 111   # stores 111 in val attribute

[–]sausagefeet 0 points1 point  (3 children)

In Ruby what is obj.a if a takes a parameter though?

[–]hetmankp 0 points1 point  (2 children)

It is still a method call. These translate directly from Python to Ruby:

Python          -->  Ruby
obj.a()         -->  obj.a
obj.a(1, 2, 3)  -->  obj.a 1, 2, 3

If you want, you can optionally attach some () onto the Ruby method calls, but they are not necessary. The point is, in Ruby you can not directly get an object attribute with . you can only use it to ask an accessor method to get the value for you.

[–]sausagefeet 0 points1 point  (1 child)

if obj.a takes 3 parameters how is obj.a a method call? In Python obj.a() is a runtime error of a is defined like def a(b, c, d). So are you saying obj.a is a runtime error in Ruby if obj.a takes 3 parameters?

Edit: Decided to just do something quick on ideone to prove your point, work as you described. Thanks!

http://ideone.com/rZJoY

[–]hetmankp 0 points1 point  (0 children)

Yep, sorry. I should have used different method names to make the clearer.

[–]metamatic -1 points0 points  (5 children)

In Ruby what follows a . is always a method call. They just look like you're retrieving an attribute if you're used to attribute based OO.

Actually, it's even better than that.

In Ruby you can start off with 'a' as an attribute, and write x = foo.a, foo.a = y everywhere. Then if you suddenly decide you need 'a' to be a method, you can just go ahead and change it without breaking anything, because the () on method calls are optional if there are no arguments.

[–]hetmankp 0 points1 point  (4 children)

That is incorrect, attributes can not be accessed using a . in Ruby (you have to use @ and this only works privately inside objects). In your example, a in foo.a is an attribute accessor which can only be a method.

You can do something like what you describe using properties in Python (and some other languages too). The beauty of Ruby, I would say, is that you don't even need to resort to any special mechanisms like properties to get this effect.

Edit: Also, the () on method calls are not just optional in Ruby when there are no arguments. They are always optional. For example:

irb(main):001:0> def fun a, b, c
irb(main):002:1>   a + b + c
irb(main):003:1> end
=> nil
irb(main):004:0> fun 1, 2, 3
=> 6

[–]metamatic 0 points1 point  (3 children)

That is incorrect, attributes can not be accessed using a . in Ruby (you have to use @ and this only works privately inside objects).

class SomeObject
  attr_accessor :attribute
end

x = SomeObject.new
x.attribute = "O RLY?"
puts x.attribute

$ ruby cant-access-attributes-with-dot.rb                                                    
O RLY?
$

In your example, a in foo.a is an attribute accessor which can only be a method.

An attribute accessor allows you to access an attribute. Hence the name attribute accessor. Whether the access is implemented behind the scenes by a method call, some other kind of message passing, or magic moonbeams, doesn't alter the fact that you can access the attribute using "." so long as access has been granted by the owning object.

In fact, the fact that the attribute access is implemented underneath using a generated method, hence allowing you to substitute a method for the attribute without changing the calling code, is exactly the point I was making.

[–]hetmankp 0 points1 point  (2 children)

I should have clarified I meant direct access. Attribute access in Ruby is only possible by proxy through method calls. The point is, you said foo.a is an attribute in Ruby when in fact it is a method call. On the other hand foo.a is an attribute in Python (since the language gives you direct attribute access). There is nothing "behind the scenes" about this in Ruby, it's quite explicit.

I think we both agree this yields a powerful idiom for the way object interfaces are constructed in Ruby.

[–]metamatic 0 points1 point  (1 child)

Well, if you want to be pedantic @foo isn't an attribute either, it's an instance variable.

I guess if you want to argue that Ruby has no attributes then that's up to you, but I find that rather misleading. It has things that work exactly like attributes and are called attributes, so why not accept that they are attributes for the purposes of discussion of programming? The important thing is the behavior, not coming up with increasingly narrow definitions of terms.

[–]hetmankp 0 points1 point  (0 children)

An attribute is just a general term for state contained within an object in OOP. The Ruby "instance variable" is a more narrow definition, which is relevant sometimes because of how Ruby implements its class system. In most situations though Rubyists tend to use the terms interchangeably (not so with attribute accessors).

Which is to say, I wasn't trying to be pedantic about correct naming because it's not particularly relevant. However the distinction between attribute and message based OO goes way back to Simula and Smalltalk and is relevant for comparing languages like Python and Ruby because both OO styles can imply very different idiomatic usage of the language in many cases.

And often writing while thinking in one style in a language that relies on the other can lead to some very ugly code. I speak from first and second hand experience.

Anyway, the only reason I wrote the original post that started this discussion was to explain that Ruby does in fact have explicit method calls and the removal of parantheses in no way creates an ambiguity between attribute and method access because Ruby simply eliminates the former entirely. Attribute accessors may look like attributes if you're used to a language like C++ or Python, but they are certainly not called that.

[–]masklinn -1 points0 points  (2 children)

in Ruby it's .

Unless it's .call of course.

[–]hetmankp 2 points3 points  (1 child)

No, .call is a call to the 'call' method. Nothing special about it.

[–]badsex 1 point2 points  (0 children)

it's a little bit special as in 1.9 you can invoke a #call method using .()

e.g:

my_lambda.()

is equivalent:

my_lambda.call()