[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

How to access to local variables in enclosing scopes?

Yuh-Ruey Chen

10/31/2008 5:41:00 PM

I'm familiar with other popular scripting languages (JS and Python),
and I'm struggling to figure out Ruby's (inanely) complex scoping
mechanisms.

My questions are within the following example:

x = 10

def foo
# how to access x from here?
end

class Klass
# how to access x from here?
def bar
# how to access x from here?
end
end

lambda { x } # I can access x here but why not in foo or Klass or bar?

I sincerely hope I'm missing something here. To me, the inability to
access local variables in an enclosing scope is very counter-
intuitive. I thought Ruby was supposed to be elegant? At the very
least, I expect some sort of "nonlocal" statement similar to Python,
e.g.

def foo
nonlocal x
# I can access x in enclosing scope now
end

And no, I don't want to have to use global variables - that would just
be pollution and would be incredibly unwieldy in large projects.
Frankly, if there was a way to access local variables in enclosing
scopes, there wouldn't even need to be a special global variable
concept.
33 Answers

David A. Black

10/31/2008 5:55:00 PM

0

Hi --

On Sat, 1 Nov 2008, Yuh-Ruey Chen wrote:

> I'm familiar with other popular scripting languages (JS and Python),
> and I'm struggling to figure out Ruby's (inanely) complex scoping
> mechanisms.

The rules are pretty simple. The def, class, and module keywords start
new local scopes. That's most of it.

> My questions are within the following example:
>
> x = 10
>
> def foo
> # how to access x from here?
>
> end
>
> class Klass
> # how to access x from here?
> def bar
> # how to access x from here?
> end
> end
>
> lambda { x } # I can access x here but why not in foo or Klass or bar?

I'm not sure what you mean by "why". Every language has scoping rules.
Ruby's rules include provision for closures, in that code blocks do
not start a new local scope and can be used as the bodies of anonymous
function objects.

There's no moral imperative, one way or the other. You're just
encountering and learning how Ruby handles these things. Relax :-)

> I sincerely hope I'm missing something here. To me, the inability to
> access local variables in an enclosing scope is very counter-
> intuitive. I thought Ruby was supposed to be elegant? At the very
> least, I expect some sort of "nonlocal" statement similar to Python,
> e.g.
>
> def foo
> nonlocal x
> # I can access x in enclosing scope now
> end

I thought you said you wanted elegance :-)

> And no, I don't want to have to use global variables - that would just
> be pollution and would be incredibly unwieldy in large projects.
> Frankly, if there was a way to access local variables in enclosing
> scopes, there wouldn't even need to be a special global variable
> concept.

Have you looked at class_eval and define_method?


class MyClass; end
m = "my_method"
MyClass.class_eval do
define_method(m) { puts "Hi from #{m}!" }
end

and similar.


David

--
Rails training from David A. Black and Ruby Power and Light:
Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL
Advancing with Rails January 19-22 Fort Lauderdale, FL *
* Co-taught with Patrick Ewing!
See http://www.r... for details and updates!

Robert Dober

10/31/2008 6:00:00 PM

0

Ruby, in it's infinite wisdom; gives you a cake that you can have and eat.

x =3D 42
def a; x end # does not work!!
Why?
Because it is efficient, there is no closure and that means one
context less to look up at runtime.

If however you want the closure (as I often do) you can still do it

x =3D 42

class << self; self end.module_eval do
define_method :a do x end
define_method :a=3D do |new_x| x =3D new_x end
end

puts a
a =3D 43
puts a
#! The next one is important !
puts x

Note the class << self; self end.module_eval
but I guess you know why I am doing this...

... class << self
end

does not create a closure.

HTH
Robert



--=20
C'est v=E9ritablement utile puisque c'est joli.

Antoine de Saint Exup=E9ry

Pit Capitain

10/31/2008 6:32:00 PM

0

2008/10/31 Yuh-Ruey Chen <maian330@gmail.com>:
> x = 10
>
> def foo
> # how to access x from here?
> end
>
> class Klass
> # how to access x from here?
> def bar
> # how to access x from here?
> end
> end
>
> And no, I don't want to have to use global variables - that would just
> be pollution and would be incredibly unwieldy in large projects.

What is the difference between your "local" variable x, which should
be accessible from everywhere, and a global variable?

Regards,
Pit

Yuh-Ruey Chen

10/31/2008 6:34:00 PM

0

Thanks for the reply.

On Oct 31, 12:55 pm, "David A. Black" <dbl...@rubypal.com> wrote:
> > My questions are within the following example:
>
> > x = 10
>
> > def foo
> >    # how to access x from here?
>
> > end
>
> > class Klass
> >    # how to access x from here?
> >    def bar
> >            # how to access x from here?
> >    end
> > end
>
> > lambda { x } # I can access x here but why not in foo or Klass or bar?
>
> I'm not sure what you mean by "why". Every language has scoping rules.
> Ruby's rules include provision for closures, in that code blocks do
> not start a new local scope and can be used as the bodies of anonymous
> function objects.

I understand that they create new scopes, but what I don't get is why
you can't access enclosing scopes! In most other languages, scopes are
hierarchical, in that nested scopes will include parent scopes.

JS:

var x = 10
function foo() {
x // I can still access x!
}

Python:

x = 10
def foo():
x # I can still access x!

Scheme:

(let ((x 10))
((lambda ()
x) ; I can still access x!
))

> There's no moral imperative, one way or the other. You're just
> encountering and learning how Ruby handles these things. Relax :-)
>
> > I sincerely hope I'm missing something here. To me, the inability to
> > access local variables in an enclosing scope is very counter-
> > intuitive. I thought Ruby was supposed to be elegant? At the very
> > least, I expect some sort of "nonlocal" statement similar to Python,
> > e.g.
>
> > def foo
> >    nonlocal x
> >    # I can access x in enclosing scope now
> > end
>
> I thought you said you wanted elegance :-)

I did explicitly say "at the very least". I'd rather not have to
specify that "nonlocal x", but if it were necessary for the sake of
compatibility, then so be it. And that's another issue: if Ruby
provides all these methods that allow me to break encapsulation, I
don't see why there isn't a method that allows me to access local vars
in enclosing scopes. That's just inflexible.

With that said, I kinda understand why x can't simply refer to
enclosing scopes, because it would be fragile: a method x would be
added which would make x refer to that method instead of the var in
the enclosing scope. That's why I think there needs to be some
equivalent of "nonlocal x". For a more slightly more elegant example:

def foo
$nonlocal.x
end

or

def foo
::x # don't know if this would conflict with anything
end

> > And no, I don't want to have to use global variables - that would just
> > be pollution and would be incredibly unwieldy in large projects.
> > Frankly, if there was a way to access local variables in enclosing
> > scopes, there wouldn't even need to be a special global variable
> > concept.
>
> Have you looked at class_eval and define_method?
>
>    class MyClass; end
>    m = "my_method"
>    MyClass.class_eval do
>      define_method(m) { puts "Hi from #{m}!" }
>    end
>
> and similar.

Yes I'm familiar with them, and I actually would like to use them, but
blocks can't have default arguments. Ideally, I would like to write
everything with blocks and avoid all this magical "new scope"
nonsense. It makes metaprogramming tricky in my experience.

Yuh-Ruey Chen

10/31/2008 6:45:00 PM

0

On Oct 31, 12:59 pm, Robert Dober <robert.do...@gmail.com> wrote:
> Ruby, in it's infinite wisdom; gives you a cake that you can have and eat.
>
> x =  42
> def a; x end # does not work!!
> Why?
> Because it is efficient, there is no closure and that means one
> context less to look up at runtime.

Not particularly convinced with that. If Python handles it with ease
and apparently no runtime performance penalty, I don't see why Ruby
can't do the same.

I'm more convinced with the fragility argument I gave in my previous
post, which is why I proposed the "nonlocal" thing.

> If however you want the closure (as I often do) you can still do it
>
> x = 42
>
> class << self; self end.module_eval do
>   define_method :a do x end
>   define_method :a= do |new_x| x = new_x end
> end
>
> puts a
> a = 43
> puts a
> #! The next one is important !
> puts x
>
> Note the class << self; self end.module_eval
> but I guess you know why I am doing this...
>
> .. class << self
>  end
>
> does not create a closure.

That's pretty clever, albeit somewhat unreadable. I'd prefer to keep
Ruby as readable as possible :)

Yuh-Ruey Chen

10/31/2008 6:49:00 PM

0

On Oct 31, 1:31 pm, Pit Capitain <pit.capit...@gmail.com> wrote:
> 2008/10/31 Yuh-Ruey Chen <maian...@gmail.com>:
>
> > x = 10
>
> > def foo
> >        # how to access x from here?
> > end
>
> > class Klass
> >        # how to access x from here?
> >        def bar
> >                # how to access x from here?
> >        end
> > end
>
> > And no, I don't want to have to use global variables - that would just
> > be pollution and would be incredibly unwieldy in large projects.
>
> What is the difference between your "local" variable x, which should
> be accessible from everywhere, and a global variable?
>
> Regards,
> Pit

The difference that the local variable doesn't have to be defined in
global scope. Only child scopes should have access to that local
variable.

def foo
a = 10
def bar
# should somehow be able to access a
end
bar
end
foo
# here, we should not be able to access foo's a, but if there is
another a in scope, we can access that

Or to put it in Python:

a = 20
def foo():
a = 10
def bar():
print a # prints 10
bar()
foo()
print a # prints 20

Stefan Lang

10/31/2008 7:19:00 PM

0

2008/10/31 Yuh-Ruey Chen <maian330@gmail.com>:

> The difference that the local variable doesn't have to be defined in
> global scope. Only child scopes should have access to that local
> variable.
>
> def foo
> a = 10
> def bar
> # should somehow be able to access a
> end
> bar
> end
> foo
> # here, we should not be able to access foo's a, but if there is
> another a in scope, we can access that
>
> Or to put it in Python:
>
> a = 20
> def foo():
> a = 10
> def bar():
> print a # prints 10
> bar()
> foo()
> print a # prints 20

The first assignment to a creates a _module_ scoped variable in Python.
It is _not_ local, it can very well be accessed from anywhere via

import some_module
some_module.a

Since it's a bad idea anyway to reassign such variables
(metaprogramming might be an exception), you can use
Ruby's constants for this purpose:

module MyModule

A = 20

# can access A from anywhere within MyModule,
# including method definitions, nested classes, etc.

end

puts MyModule::A # access from outside of MyModule

At least anything that looks like local variable assignment
in Ruby is actually local variable assignment. The thing to
learn is that "module", "class" and "def" create new, empty
local scopes, they have no enclosing scope with regards to
local variables.

It is generally a better idea to genuinely try to understand
a new language and its idioms instead of bending it to ones
expectations from the beginning.

Stefan

Mike Gold

10/31/2008 7:20:00 PM

0

Yuh-Ruey Chen wrote:
>
>> � �MyClass.class_eval do
>> � � �define_method(m) { puts "Hi from #{m}!" }
>> � �end
>>
>> and similar.
>
> Yes I'm familiar with them, and I actually would like to use them, but
> blocks can't have default arguments. Ideally, I would like to write
> everything with blocks and avoid all this magical "new scope"
> nonsense. It makes metaprogramming tricky in my experience.

I use often use Class.new and define_method for just the reasons you
describe: metaprogramming most naturally goes with locals bound to
closures. This style also avoids a handful of potential problems with
the "regular" way of using different scopes, in particular name
conflicts and lack of true read-only protection for variables.

I was told that I wasn't doing things in a "rubyish" way. Disregard
that BS. Use closures and define_method for metaprogramming. Your code
will be better.

Note 1.9 allows default arguments for lambdas as well as a &block
parameter. 1.8.7 allows a &block parameter where 1.8.6 does not. For
1.8.x you can get the same effect of default arguments like this

class << self
define_method(:foo) { |*args|
a, b =
case args.size
when 0
["fred", "barney"]
when 1
[args.first, "barney"]
when 2
args
else
raise ArgumentError,
"wrong number of arguments (#{args.size} for 2)"
end

puts "foo: #{a} #{b}"
}
end

foo #=> foo: fred barney
foo :a #=> foo: a barney
foo :a, :b #=> foo: a b

--
Posted via http://www.ruby-....

Yuh-Ruey Chen

10/31/2008 7:34:00 PM

0

On Oct 31, 2:18 pm, Stefan Lang <perfectly.normal.hac...@gmail.com>
wrote:
> 2008/10/31 Yuh-Ruey Chen <maian...@gmail.com>:
>
>
>
> > The difference that the local variable doesn't have to be defined in
> > global scope. Only child scopes should have access to that local
> > variable.
>
> > def foo
> >        a = 10
> >        def bar
> >                # should somehow be able to access a
> >        end
> >        bar
> > end
> > foo
> > # here, we should not be able to access foo's a, but if there is
> > another a in scope, we can access that
>
> > Or to put it in Python:
>
> > a = 20
> > def foo():
> >        a = 10
> >        def bar():
> >                print a # prints 10
> >        bar()
> > foo()
> > print a # prints 20
>
> The first assignment to a creates a _module_ scoped variable in Python.
> It is _not_ local, it can very well be accessed from anywhere via
>
>     import some_module
>     some_module.a

Well then just wrap that example in another function:

def scope()
a = 20
def foo():
a = 10
def bar():
print a # prints 10
bar()
foo()
print a # prints 20
scope()

My point was that scopes nest as expected.

> Since it's a bad idea anyway to reassign such variables
> (metaprogramming might be an exception), you can use
> Ruby's constants for this purpose:
>
>     module MyModule
>
>         A = 20
>
>         # can access A from anywhere within MyModule,
>         # including method definitions, nested classes, etc.
>
>     end
>
>     puts MyModule::A  # access from outside of MyModule
>
> At least anything that looks like local variable assignment
> in Ruby is actually local variable assignment. The thing to
> learn is that "module", "class" and "def" create new, empty
> local scopes, they have no enclosing scope with regards to
> local variables.
>
> It is generally a better idea to genuinely try to understand
> a new language and its idioms instead of bending it to ones
> expectations from the beginning.
>
> Stefan

I am trying to understand Ruby with some metaprogramming practice. But
I still feel that this is a really backwards step in language design.

It's like telling me I should use GOTO in original BASIC instead of
trying to bend the language to simulate while loops. Would any sane
modern programmer do that?

Yuh-Ruey Chen

10/31/2008 7:38:00 PM

0

On Oct 31, 2:18 pm, Stefan Lang <perfectly.normal.hac...@gmail.com>
wrote:
> Since it's a bad idea anyway to reassign such variables
> (metaprogramming might be an exception), you can use
> Ruby's constants for this purpose:
>
>     module MyModule
>
>         A = 20
>
>         # can access A from anywhere within MyModule,
>         # including method definitions, nested classes, etc.
>
>     end
>
>     puts MyModule::A  # access from outside of MyModule

Oops, forgot to reply to this bit.

This is not what I'm really looking for. I don't care about accessing
variables within another unrelated scope - in fact, unless explicitly
allowed, it should not be allowed. I only care about accessing
variables in enclosing scopes.