local variables vs. methods

H

Henrik Schmidt

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
 
H

Hakusa

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!
 
H

Henrik Schmidt

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.
 
H

Hakusa

What if I
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?
 
H

Henrik Schmidt

Henrik said:
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 :)


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.
 
H

Henrik Schmidt

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.
 
H

Hakusa

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.
 
H

Henrik Schmidt

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?
 
T

Trans

It is.




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.


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.
 
T

Trans

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?

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.
 
B

Brian Candler

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


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.
 
H

Henrik Schmidt

Brian said:
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 "$"

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.
(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.

I found this googling
(http://talklikeaduck.denhaven2.com/xml/rss20/article/256/feed.xml):

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
 
H

Henrik Schmidt

Trans said:
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.

I didn't know you could do that. That's kinda horrible :)
 
D

Daniel DeLorme

Henrik said:
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
 
D

dblack

Hi --

I found this googling
(http://talklikeaduck.denhaven2.com/xml/rss20/article/256/feed.xml):

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.

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

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
L

Logan Capaldo

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top