Avoiding scope pitfall

Here’s a simple snippet of code of a ruby scope pitfall (bug?) that I
keep stumbling upon.
Here a “global” function is invoked in two different contexts and
returns different behavior due to self being set differently.


#!/usr/bin/env ruby

@x = ‘hello global’

def global_func
puts “global_func: class:#{self.class} x=#@x
end

class A
def test_scope
global_func
end
end

global_func

a = A.new
a.test_scope

I’m wondering if this is intended behavior in the language. If
intended, what’s the best approach to avoid running into this? I’m
currently porting code from other languages with different (more
correct to me) scoping, and this keeps tripping me.

On 05.04.2007 12:37, gga wrote:

def global_func

a = A.new
a.test_scope

I’m wondering if this is intended behavior in the language.

It is intended:

13:16:34 [Temp]: ./scope.rb
#<Method: Object#global_func>
global_func: class:Object x=hello global
global_func: class:A x=
13:16:50 [Temp]: cat scope.rb
#!/usr/bin/env ruby

@x = ‘hello global’

def global_func
puts “global_func: class:#{self.class} x=#@x
end

p method( :global_func )

class A
def test_scope
global_func
end
end

global_func

a = A.new
a.test_scope
13:16:53 [Temp]:

Every top level method is defined in class Object and thus inherited by
all classes.

If
intended, what’s the best approach to avoid running into this? I’m
currently porting code from other languages with different (more
correct to me) scoping, and this keeps tripping me.

Do not use instance variables in global functions. As simple as that.

Regards

robert

On Apr 5, 12:37 pm, “gga” [email protected] wrote:

def global_func

a = A.new
a.test_scope

I don’t know why you call that a pitfall. I tested the behaviour and
it did exactly what I expected.

I think you are confused by what you call “global functions”. You’d
better forget them: Ruby doesn’t have global functions. It only has
methods. When you define a so-called “global function”, you’re just
defining a method at the top level, which is in an object of class
Oject, called main. Due to the scope rules at the top level, defining
a method there adds it to the class Object, as an instance method.
Nothing strange there so far.

Now when you define class A, it becomes automatically a subclass of
Object, as you probably know, since Object is the root of the class
hierarchy. As a subclass of Object, it gains all Object’s instance
methods, including the one you just defined: “global_func” (you can
check that by running a.methods). So when you call “global_func” in
the test_scope method, you are actually calling an inherited method,
which will run then in the scope of the a object you created, as one
would expect from inherited methods
! So when you run a.test_scope,
global_func will run in the scope of a, where self.class is A and @x
hasn’t been assigned and thus returns nil. This is perfectly normal,
given the rules of the Ruby language.


I’m wondering if this is intended behavior in the language. If
intended, what’s the best approach to avoid running into this? I’m
currently porting code from other languages with different (more
correct to me) scoping, and this keeps tripping me.

Well, the answer is that yes, it is the intended behaviour in the
language, in the sense that it is completely valid behaviour according
to the rules of the language. Just forget about global functions: they
are just methods of the class Object, which become available to all
objects only due to inheritance, and thus follow all the rules of
inherited methods, included the rule that when called in an instance
of a subclass, they run in the scope of that instance. Normally
functions defined on the top level don’t care about this, as their
behaviour normally does not depend on the state of the object they’re
called in (like “puts”, which will output to $stdout anyway, since
$stdout is a true global variable which refers to the same IO object
everywhere). For others, we just don’t expect them to behave
differently (no one would expect the “class” method to return anything
but the class of the object they’re in).

A way to remember that behaviour might be to remember that methods
defined on the top level act as if they had a “class Object … end”
surrounding their definition. That might help you remember that they
are inherited to other classes, rather than simply have a global
scope.

And if you don’t want that behaviour, just have your top-level methods
never rely on the scope they’re running in. That can be done for
instance by only have them use global variables (but watch your thread
safety go bye-bye if you write threaded code :slight_smile: ) and local variables
(which are local to the scope of the method anyway, wherever that
method’s used).

Hopefully with this you should understand why this behaviour is
actually correct, when you think clearly about how the Ruby language
works (i.e. it’s internally consistent). One thing I’ve noticed and
liked about Ruby is how consistent it is, and how few exceptions and
little rules you need to learn when you understand its basic
principles. You just need to be consistent yourself in how you are
applying those basic principles to your programs.

Cheers,

Christophe.