Forum: Ruby local variables vs. methods

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.
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 15:36
(Received via mailing list)
Hi there,

I've been playing around with Ruby for a while, but there's still one
particular feature of the language that doesn't make sense to me. If you
write a class containing a method and a class with the same name, the
interpreter will pick the variable over the method, unless you
specifically tells it not to. For example,

class Foo

   def output
     puts foo            # "foo"
     foo = 42
     puts foo            # 42
     puts self.foo       # "foo"
     puts foo()          # "foo"
   end

   def foo
     "foo"
   end

end

Foo.new.output

As seen above, there are several ways of getting around this, but this
is the question: Why is this behaviour useful? As I see it, it's bad
practice to give a method and a local variable the same name. At least I
can't think of an example where it would make sense. Why not simply
disallow this or at least have the interpreter issue a warning?

Best regards,
   Henrik Schmidt
79941ab5294a74685db85edfe0db8b7f?d=identicon&s=25 Hakusa@gmail.com (Guest)
on 2007-05-26 16:56
(Received via mailing list)
If a language stops you from doing something just because it's bad
practice, then the language is treating you like an idiot. What if I
wanted to override a function for a little while? I could assign it a
new value and use the same methods of a different class!
481b8eedcc884289756246e12d1869c1?d=identicon&s=25 Francis Cianfrocca (blackhedd)
on 2007-05-26 17:14
(Received via mailing list)
On 5/26/07, Henrik Schmidt <no.spam@nspmaxyz.abc> wrote:
>
>    end
>
> end
>
> Foo.new.output




C++ does the same thing, you know. It's not mysterious if you understand
how
the language's scoping rules work.
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 18:01
(Received via mailing list)
Hakusa@gmail.com wrote:
> If a language stops you from doing something just because it's bad
> practice, then the language is treating you like an idiot.

Fine, so don't stop be. Warn me that I'm doing something which is
probably a programmer error 99% of the time. Ruby stops me from all
sorts of things I can do in Perl. I think that's a good thing, since I'm
a horrible Perl programmer :)

>What if I
> wanted to override a function for a little while? I could assign it a
> new value and use the same methods of a different class!
>

Then you'll just override it. My question is, why would you want to
override a method with a variable? I have no problem with overloading a
method with another method, and neither should the interpreter.
79941ab5294a74685db85edfe0db8b7f?d=identicon&s=25 Hakusa@gmail.com (Guest)
on 2007-05-26 18:25
(Received via mailing list)
> >What if I
> > wanted to override a function for a little while? I could assign it a
> > new value and use the same methods of a different class!
>
> Then you'll just override it. My question is, why would you want to
> override a method with a variable? I have no problem with overloading a
> method with another method, and neither should the interpreter.

What if instead of printing bar's output with puts bar, bar was a
local variable so it printed an error message alerting the user that
something wrong has happened.

I don't really know when it would be useful, but who knows. What if
someday I find that it is?
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 18:35
(Received via mailing list)
Henrik Schmidt wrote:
>> wanted to override a function for a little while? I could assign it a
>> new value and use the same methods of a different class!
>>
>
> Then you'll just override it. My question is, why would you want to
> override a method with a variable? I have no problem with overloading a
> method with another method, and neither should the interpreter.

In fact, now that I think about it, how would you override a method from
one class with a variable in another class making use of this feature?

Last year I asked a question also pertaining to variable/method
ambiguity. At the time I thought one solution might be to force
parentheses on all methods. I got some very good answers why this was a
bad idea. The best answer was, IMHO, that it would make the private and
protected methods look strange and unintuitive. The implication of that
was of course, that you could create methods like private and protected
and have them behave as keywords or even overload these methods and have
them behave differently. I wasn't aware of that of the time, and I think
that's pretty cool.

Basically, I'm looking for the same thing here. What possible use can
there be for this feature? I don't get it.
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 18:46
(Received via mailing list)
Hakusa@gmail.com wrote:
>>> What if I
>>> wanted to override a function for a little while? I could assign it a
>>> new value and use the same methods of a different class!
>> Then you'll just override it. My question is, why would you want to
>> override a method with a variable? I have no problem with overloading a
>> method with another method, and neither should the interpreter.
>
> What if instead of printing bar's output with puts bar, bar was a
> local variable so it printed an error message alerting the user that
> something wrong has happened.

I think a nicer way of doing this would be to overload bar with a method
that throws an exception.

> I don't really know when it would be useful, but who knows. What if
> someday I find that it is?

If you can do the same thing with a method I don't see the problem.
Also, it would eliminate (or at least warn) when you do this:

def foo
   42
end

puts foo # -> 42
foo = 24 if false;
puts foo # -> nil

I would like to be warned in this situation, as it's fairly likely I'm
doing something I didn't intend to do.
79941ab5294a74685db85edfe0db8b7f?d=identicon&s=25 Hakusa@gmail.com (Guest)
on 2007-05-26 18:47
(Received via mailing list)
> Basically, I'm looking for the same thing here. What possible use can
> there be for this feature? I don't get it.

Thought of something:
I still don't really know how to use a proc or anything like that, but
it seems to me ...

proc = Proc.new # How you initialize a proc makes me think that a proc
is just another type of a variable.

So you could overload your method with a variable so that a proc which
does a different thing is called. If you can see why overloading with
a method could be good, you can see why this could be good.

But note that I'm not all that experienced either, so I could be
entirely wrong. But I still think that someday, it just might be
helpful.
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 19:06
(Received via mailing list)
Hakusa@gmail.com wrote:
>> Basically, I'm looking for the same thing here. What possible use can
>> there be for this feature? I don't get it.
>
> Thought of something:
> I still don't really know how to use a proc or anything like that, but
> it seems to me ...
>
> proc = Proc.new # How you initialize a proc makes me think that a proc
> is just another type of a variable.

It is.

>
> So you could overload your method with a variable so that a proc which
> does a different thing is called. If you can see why overloading with
> a method could be good, you can see why this could be good.

Well, if you would like proc to do something different then just assign
a different Proc to it. Also, I don't think you can overload proc with a
method. The variable always wins.

> But note that I'm not all that experienced either, so I could be
> entirely wrong. But I still think that someday, it just might be
> helpful.

I can't think of any way, contrived or otherwise, that would make this
helpful, but I'm fairly inexperienced myself. Let's say I accept your
argument. Why not issue a warning?
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Trans (Guest)
on 2007-05-26 20:26
(Received via mailing list)
On May 26, 1:05 pm, Henrik Schmidt <no.s...@nspmaxyz.abc> wrote:
>
> method. The variable always wins.
>
> > But note that I'm not all that experienced either, so I could be
> > entirely wrong. But I still think that someday, it just might be
> > helpful.
>
> I can't think of any way, contrived or otherwise, that would make this
> helpful, but I'm fairly inexperienced myself. Let's say I accept your
> argument. Why not issue a warning?


  class X
    def foo; "blah"; end

    def bar
      if something_or_another
        foo = "big blah"
      end
      puts foo
    end
  end

T.
45196398e9685000d195ec626d477f0e?d=identicon&s=25 Trans (Guest)
on 2007-05-26 20:31
(Received via mailing list)
On May 26, 9:35 am, Henrik Schmidt <no.s...@nspmaxyz.abc> wrote:
>    def output
>
> end
>
> Foo.new.output
>
> As seen above, there are several ways of getting around this, but this
> is the question: Why is this behaviour useful? As I see it, it's bad
> practice to give a method and a local variable the same name. At least I
> can't think of an example where it would make sense. Why not simply
> disallow this or at least have the interpreter issue a warning?

Perhaps we could ask about this from the opposite perspective. Would
it not be useful to define local methods --in method scope?

  def foo
    def bar; 10; end
    bar
  end

In effect then, a method is nothing more than a lazy variable having
it's own specific rules of scope. There's really no good reason to
restrict name clash.

T.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-26 20:35
(Received via mailing list)
On Sun, May 27, 2007 at 01:00:03AM +0900, Henrik Schmidt wrote:
> >wanted to override a function for a little while? I could assign it a
> >new value and use the same methods of a different class!
> >
>
> Then you'll just override it. My question is, why would you want to
> override a method with a variable? I have no problem with overloading a
> method with another method, and neither should the interpreter.

The trouble is:

(1) All objects are descendants of Object, which in turn mixes in
Kernel.
Many other objects mix in Enumerable, Comparable and other modules.

This means that a typical object has roughly a googol different methods
already present, and it's very easy to pick a local variable name which
happens to collide with one. Silently ignoring this "just works". Giving
an
error would be very annoying; instead of "id = 9" I'd have to change it
to
"my_id = 9" or somesuch. In the end I'd prefix all local variables with
my_... which would be worse than using something perlish like "$"

(2) From a very practical perspective, it's extremely difficult for Ruby
to
generate these warnings.

The problem is: Ruby is a completely dynamic language, and at parse time
you
have no idea what methods an object has. The decision as to 'local
variable'
or 'method' is made statically, based on inspection of the code
*before* it's executed, which means before any classes and methods have
been
created.

To perform the check you're asking for, Ruby would have to add extra
run-time code after *every* local variable access to perform a method
search
just to check if a method with the same name exists. Example:

   10000.times do
     x = flurble()
     y = y + x
   end

Each time round the loop, the call to flurble() may have ended up
defining a
method called 'x' in the current object. So every time round the loop,
you'd
have to check, at the point where 'x' was read and/or assigned, that
there
was currently no method called 'x' (or 'x=') in the object.

Regards,

Brian.
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 23:25
(Received via mailing list)
Trans wrote:
>> class Foo
>>      "foo"
>> disallow this or at least have the interpreter issue a warning?
> it's own specific rules of scope. There's really no good reason to
> restrict name clash.
>
> T.
>
>

I didn't know you could do that. That's kinda horrible :)
8ef610a75918fc724bec1155f6d5a5ab?d=identicon&s=25 Henrik Schmidt (Guest)
on 2007-05-26 23:28
(Received via mailing list)
Brian Candler wrote:
>>> wanted to override a function for a little while? I could assign it a
>
> This means that a typical object has roughly a googol different methods
> already present, and it's very easy to pick a local variable name which
> happens to collide with one. Silently ignoring this "just works". Giving an
> error would be very annoying; instead of "id = 9" I'd have to change it to
> "my_id = 9" or somesuch. In the end I'd prefix all local variables with
> my_... which would be worse than using something perlish like "$"

Hmmm, I didn't think of that. Local variable/method ambiguity is only a
problem within the class. If you mix in something, you need to be aware
of which methods these mixins have. Otherwise you, or anyone who uses
your class might get unexpected results. If I decide to give all my
classes an id method, someone calling the my_class.id will probably not
get the result he wanted. My point is, that you need to be aware that
you're actually overloading a mixed in or inherited method anyway.
Normally I would think that's a reasonable requirement, and anyone can
look through Kernel and Object to get an idea of which method-names are
dangerous. Of course, it might have been a good idea to give these
methods less generic names. I'm not sure why they deprecated the id
method, but I guess it might have something to do with this. Can anyone
clue me in?

Anyway, it could be partially solved by only issuing a warning if your
local variable is actually shadowing a local method, but I definitely
see your point.

> To perform the check you're asking for, Ruby would have to add extra
> have to check, at the point where 'x' was read and/or assigned, that there
> was currently no method called 'x' (or 'x=') in the object.
>

I found this googling
(http://talklikeaduck.denhaven2.com/xml/rss20/artic...

class Point
    attr_accessor :x, :y

    def initialize(init_x,init_y)
       x, y = init_x, init_y
    end
end

p Point.new(10,10).x => nil

That's a fair mistake. The compiler doesn't know about the attr_accessor
so it thinks x and y are local variables. What I don't understand is,
that even if you put in the appropriate methods instead of relying on
attr_accessor, it still doesn't work.

Anyway, I still think one could make a sensible warning system to be run
at compile-time that would catch most of the more obvious programmer
errors, but it wouldn't be perfect. The counter-argument would be that
we're probably better off leaving it alone so people will fall into the
trap, learn what happens, and stop doing it early on. Then again, a
warning would probably lead them to the path of enlightenment earlier.

I still wouldn't mind getting a warning, but I didn't realize all the
implications of trying to fix it, so I guess I can accept that since
there seem to be no way to fix it properly without sigils anyway, it
should be left as it is.

Thanks for the explanation,
   Henrik Schmidt
E0526a6bf302e77598ef142d91bdd31c?d=identicon&s=25 Daniel DeLorme (Guest)
on 2007-05-27 04:25
(Received via mailing list)
Henrik Schmidt wrote:
> Basically, I'm looking for the same thing here. What possible use can
> there be for this feature? I don't get it.

Sometimes I use that "feature" to cache a method's response. e.g.:
   foo = self.foo
   #do a bunch of things with foo without having to call the method

But I think the ability to override a method with a local variable is
not so much a feature as a side-effect of the synctatic simplicity of
invoking a method as only "foo" instead of "foo()" or "self.foo"

Like you, I am also a little annoyed by the inconsistence:
   foo        #variable or method
   foo()      #method
   self.foo   #method
   foo=1      #variable
   self.foo=1 #method
   foo=(1)    #variable

but I think consistence is a small price to pay for a syntax that
doesn't get in your way in 99% of cases.

Daniel
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2007-05-27 04:42
(Received via mailing list)
Hi --

On Sun, 27 May 2007, Henrik Schmidt wrote:

>
> p Point.new(10,10).x => nil
>
> That's a fair mistake. The compiler doesn't know about the attr_accessor so
> it thinks x and y are local variables. What I don't understand is, that even
> if you put in the appropriate methods instead of relying on attr_accessor, it
> still doesn't work.

It does work -- it assigns init_x and init_y to the local variables x
and y, which is what you've asked it to do :-)  If you want to call x=
and y=, you can do:

   self.x, self.y = init_x, init_y

or, since this is just a simple accessor:

   @x, @y = init_x, init_y

> Anyway, I still think one could make a sensible warning system to be run at
> compile-time that would catch most of the more obvious programmer errors, but
> it wouldn't be perfect. The counter-argument would be that we're probably
> better off leaving it alone so people will fall into the trap, learn what
> happens, and stop doing it early on. Then again, a warning would probably
> lead them to the path of enlightenment earlier.

My counterargument is that you'd then have the system fighting itself.
On the one hand, it's engineered so that there's a kind of fluidity
between method and local variable names, in certain circumstances; but
on the other hand, you've start getting warnings if you avail yourself
of that fluidity.

Even a small test suite should expose problems in this area quickly.
You'd immediately see that your new Point object wasn't doing what you
expected, and you could track down the problem and fix it.  Meanwhile
you'd be able to reap the advantages of the system without being
interrupted by warnings.


David
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (Guest)
on 2007-05-27 14:49
(Received via mailing list)
On Sun, May 27, 2007 at 06:25:09AM +0900, Henrik Schmidt wrote:
> Hmmm, I didn't think of that. Local variable/method ambiguity is only a
> problem within the class. If you mix in something, you need to be aware
> of which methods these mixins have. Otherwise you, or anyone who uses
> your class might get unexpected results. If I decide to give all my
> classes an id method, someone calling the my_class.id will probably not
> get the result he wanted.

I disagree. If someone uses your class, then he or she will look at your
rdoc documentation, and see you have an 'id' method and, if it's what
they
want to use, will call it.

Now of course, this hides the system 'id' method (which happens to be
deprecated anyway). But you'll find this done where it's appropriate,
e.g.
ActiveRecord uses foo.type = bar

However, this has no impact at all on local variables. If a user of your
class decides to write "id = 123", then this *always* generates a local
variable, and this can be seen immediately, by inspection of the code
locally, without having to cross-reference to the class definition.

Equally, if you do

  class Myclass < Other
    def foo
      ...
      x = 1
      ...
    end
  end

then you don't have to worry whether class Other has a method called 'x'
or
not (or if it doesn't today, that perhaps it might grow one tomorrow).
It's
unambiguously a local variable called 'x' that you're using.

> That's a fair mistake. The compiler doesn't know about the attr_accessor
> so it thinks x and y are local variables. What I don't understand is,
> that even if you put in the appropriate methods instead of relying on
> attr_accessor, it still doesn't work.

Because of the very simple rule that says "a statement of the form 'x =
...'
*always* makes x a local variable from this point until the end of the
current scope". If you want to call a method called 'x=' then you always
need to do self.x=

I sympathise with your concern - a few times I've written some code
which
fails unit tests and had to stare at it for a while, before realising
what
I'd done. This is particularly true for ActiveRecord, which has lots of
accessors, and where you might be tempted to write "something = nil"
instead
of "self.something = nil"

However, if you want a warning for the code above, I think a better one
would be "local variable x assigned but not used". Actually that
shouldn't
be too hard to add, and I imagine it could indeed catch a few silly
typos.

Regards,

Brian.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2007-05-27 16:45
(Received via mailing list)
On Sat, May 26, 2007 at 10:35:03PM +0900, Henrik Schmidt wrote:
> As seen above, there are several ways of getting around this, but this
> is the question: Why is this behaviour useful? As I see it, it's bad
> practice to give a method and a local variable the same name. At least I
> can't think of an example where it would make sense. Why not simply
> disallow this or at least have the interpreter issue a warning?
>
Just to throw out another reason, that I don't think has been mentioned
yet, it would be expensive. For the interpreter to issue a warning about
local variables shadowing method names, it would require the interpreter
to do a method lookup on _every_ variable assignment. And it couldn't
even be at parse time, it would have to be every time. Eg:

class A
  def bar
    foo = 1 # the interpreter doesn't yet know that there will be a foo
            # method
    foo
  end

  def foo
    "foo"
  end
end

# Therefore, it has to do at least 2 method lookups everytime I call
# bar below, one for bar itself, and one to make sure foo isn't a method

v = A.new
v.bar
v.bar
v.bar

It also can't cache the need to warn, because I can remove_method :foo,
making the warning incorrect.

Method lookup on every assign would not be a cheap operation. (I know we
don't usually like to talk about speed here, but this would be _really_
bad).
This topic is locked and can not be replied to.