Forum: Ruby How to access to local variables in enclosing scopes?

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.
Yuh-Ruey C. (Guest)
on 2008-10-31 19:45
(Received via mailing list)
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.
David A. Black (Guest)
on 2008-10-31 19:56
(Received via mailing list)
Hi --

On Sat, 1 Nov 2008, Yuh-Ruey C. 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.

>   # 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
Robert D. (Guest)
on 2008-10-31 20:01
(Received via mailing list)
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.

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.

HTH
Robert



--
C'est véritablement utile puisque c'est joli.

Antoine de Saint Exupéry
Pit C. (Guest)
on 2008-10-31 20:33
(Received via mailing list)
2008/10/31 Yuh-Ruey C. <removed_email_address@domain.invalid>:
>        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 C. (Guest)
on 2008-10-31 20:35
(Received via mailing list)
Thanks for the reply.

On Oct 31, 12:55 pm, "David A. Black" <removed_email_address@domain.invalid> 
wrote:
> >    # how to access x from here?
> 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!
     ))

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

>    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 C. (Guest)
on 2008-10-31 20:50
(Received via mailing list)
On Oct 31, 12:59 pm, Robert D. <removed_email_address@domain.invalid> 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.

> a = 43
> does not create a closure.
That's pretty clever, albeit somewhat unreadable.  I'd prefer to keep
Ruby as readable as possible :)
Yuh-Ruey C. (Guest)
on 2008-10-31 20:52
(Received via mailing list)
On Oct 31, 1:31 pm, Pit C. <removed_email_address@domain.invalid> wrote:
> >        def bar
> 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 L. (Guest)
on 2008-10-31 21:20
(Received via mailing list)
2008/10/31 Yuh-Ruey C. <removed_email_address@domain.invalid>:

> end
>                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 G. (Guest)
on 2008-10-31 21:21
Yuh-Ruey C. 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
Yuh-Ruey C. (Guest)
on 2008-10-31 21:36
(Received via mailing list)
On Oct 31, 2:18 pm, Stefan L. <removed_email_address@domain.invalid>
wrote:
> >        def bar
> > a = 20
>
>     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.

>
> 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 C. (Guest)
on 2008-10-31 21:40
(Received via mailing list)
On Oct 31, 2:18 pm, Stefan L. <removed_email_address@domain.invalid>
wrote:
>
>     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.
Stefan L. (Guest)
on 2008-10-31 22:12
(Received via mailing list)
2008/10/31 Yuh-Ruey C. <removed_email_address@domain.invalid>:
>        print a # prints 20
> scope()

FWIW, Ruby doesn't have nested procedures like that.

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

I don't think it's a good analogy. Ruby is more oriented towards
little objects and message passing than Pascal-like nested
procedures.

I'm sure we could work out an elegant solution to some higher
level goal than "accessing local variables", especially when it
comes to metaprogramming.

Stefan
Mike G. (Guest)
on 2008-10-31 22:20
Yuh-Ruey C. wrote:
> 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.

Just to verify we are on the same page: define_method solves all your
problems, right?  If you don't wish to use Ruby 1.9 (which will be
officially released in two months) then the corner case is default
arguments, which can be done via the example I gave.

Keep in mind that you are looking at just one side of the coin.  There's
a flip side to it.  Preventing local variables from willy-nilly
infecting your methods is a Good Thing, and is usually what you want.

> 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

If you really want x to be globally accessible then you should do

require 'ostruct'
GLOBALS = OpenStruct.new
GLOBALS.x = 10

def foo
  GLOBALS.x
end

class Klass
  GLOBALS.x
  def bar
    GLOBALS.x
  end
end

This makes your intention explicit.  If you know python, you've probably
heard the phrase, "Explicit is better than implicit."  :)
Yuh-Ruey C. (Guest)
on 2008-10-31 23:10
(Received via mailing list)
On Oct 31, 3:19 pm, Mike G. <removed_email_address@domain.invalid> wrote:
> Yuh-Ruey C. wrote:
> > 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.
>
> Just to verify we are on the same page: define_method solves all your
> problems, right?  If you don't wish to use Ruby 1.9 (which will be
> officially released in two months) then the corner case is default
> arguments, which can be done via the example I gave.

I was not aware that this was being fixed. Thanks for the tip.

I've also noticed that there is a break in symmetry between |def| and |
define_method| besides this block issue. Compare:

x = 0
C.define_method(:foo) do
  puts x  # refers to outside x
  puts self.x  # refers and calls this object's method x
end

with:

x = 0
C.module_eval do
  def foo
    # no way to refer to outside x, but something like |scope.x| would
be nice for symmetry
    puts x  # refers and calls this object's method x
  end
end

These would be functionally equivalent if there were some way,
analagous to the former's |self.x|, to access vars in enclosing
scopes. I'd argue that such a symmetry would be elegant.

> Keep in mind that you are looking at just one side of the coin.  There's
> a flip side to it.  Preventing local variables from willy-nilly
> infecting your methods is a Good Thing, and is usually what you want.

I agree, it's a good thing.

x = 10
def foo
  x # accessed outside x
end

wouldn't be a good thing, because adding a new method called "x" would
ruin it.

But something like:

x = 10
def foo
  scope.x
end

would not be as fragile, assuming |scope| is a new builtin method that
does what you'd expect it do.

> class Klass
>   GLOBALS.x
>   def bar
>     GLOBALS.x
>   end
> end
>
> This makes your intention explicit.  If you know python, you've probably
> heard the phrase, "Explicit is better than implicit."  :)

Except I do NOT want x to be globally accessible. I just want to to be
accessible be child scopes. If I wanted it to be globally accessible,
then I would just use global variables.
Yuh-Ruey C. (Guest)
on 2008-10-31 23:20
(Received via mailing list)
On Oct 31, 3:09 pm, Stefan L. <removed_email_address@domain.invalid>
wrote:
> >                bar()
> >        foo()
> >        print a # prints 20
> > scope()
>
> FWIW, Ruby doesn't have nested procedures like that.

According to my copy of Ruby, it does.

> > 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?
>
> I don't think it's a good analogy. Ruby is more oriented towards
> little objects and message passing than Pascal-like nested
> procedures.

Then why the first-class code blocks? Certainly makes Ruby a
functional language, if only for blocks.

> I'm sure we could work out an elegant solution to some higher
> level goal than "accessing local variables", especially when it
> comes to metaprogramming.

The ability to access local vars in enclosing scopes is strictly
unnecessary. But the same could also be said for instance_variable_get/
defined?/set.
Stefan L. (Guest)
on 2008-11-01 00:22
(Received via mailing list)
2008/10/31 Yuh-Ruey C. <removed_email_address@domain.invalid>:
>> >                def bar():
>> >                        print a # prints 10
>> >                bar()
>> >        foo()
>> >        print a # prints 20
>> > scope()
>>
>> FWIW, Ruby doesn't have nested procedures like that.
>
> According to my copy of Ruby, it does.

Looks can be deceiving:

    $ cat nest_def.rb
    class Foo
      def a
        puts "in a"
        def b
          puts "in b"
        end
        b
      end
    end

    f = Foo.new
    p Foo.instance_methods(false)
    f.a
    p Foo.instance_methods(false)
    f.b
    $ ruby nest_def.rb
    ["a"]
    in a
    in b
    ["b", "a"]
    in b

A def inside a method definition defines a method in
self's class. It is not local to the enclosing method definition.

>
> Then why the first-class code blocks? Certainly makes Ruby a
> functional language, if only for blocks.

Yes, Ruby has a good deal of functional features and they
fit in very nicely with the current scoping rules.

Although I'm not sure what you mean with "first class" code
blocks. The block syntax is actually tied to message passing.

>> I'm sure we could work out an elegant solution to some higher
>> level goal than "accessing local variables", especially when it
>> comes to metaprogramming.
>
> The ability to access local vars in enclosing scopes is strictly
> unnecessary. But the same could also be said for instance_variable_get/
> defined?/set.

Ruby does let you access local vars in enclosing scopes. It's
only that your definition of enclosing scope differs from Ruby's.

Try to write some programs and you'll see it all fits together nicely.

Many design decisions may seem arbitrary. We could also debate
the whole day about explicit self, assigning to local variables in
outer scopes, etc. in Python. It's useless.

In the end, you'll find out what looks like a big problem now
actually isn't. The most important usecase for your desired
scoping rules would be nested functions, and Ruby doesn't
have them. And for the other use cases, constants, class-
and module instance variables suffice. OTOH, you can nest
blocks as much as you like and scoping is what you expect.

Stefan
Mike G. (Guest)
on 2008-11-01 00:44
>> Keep in mind that you are looking at just one side of the coin. �There's
>> a flip side to it. �Preventing local variables from willy-nilly
>> infecting your methods is a Good Thing, and is usually what you want.
>
> I agree, it's a good thing.

Here you agree, but earlier in the same post you disagree:

Yuh-Ruey C. wrote:
> I've also noticed that there is a break in symmetry between |def| and |
> define_method| besides this block issue. Compare:

The break in symmetry is intentional because it prevents local variables
from willy-nilly infecting your methods, which you agreed is a good
thing.

>
> These would be functionally equivalent if there were some way,
> analagous to the former's |self.x|, to access vars in enclosing
> scopes. I'd argue that such a symmetry would be elegant.
>
>
> x = 10
> def foo
>   x # accessed outside x
> end
>
> wouldn't be a good thing, because adding a new method called "x" would
> ruin it.
>
> But something like:
>
> x = 10
> def foo
>   scope.x
> end
>
> would not be as fragile, assuming |scope| is a new builtin method that
> does what you'd expect it do.
>

But then you've missed out on the advantage of lexical local variables.
What you propose takes the "lexical" out of "lexical scope".  It is just
nested variables which are visible from other scopes.

If that is what you wish, then you do not mean local (lexical)
variables.  You mean shared data, and Ruby wants you to be explicit
about when you want shared data that will "infect" your method and class
definitions.  This is a good thing.

require 'ostruct'

module Top
  SHARED = OpenStruct.new
  SHARED.x = 10

  class Foo
    def bar
      p SHARED.x
    end
  end
end

Top::Foo.new.bar  #=> 10

Lexical variables are used when you most definitely DO NOT want that.
It is useful to have this distinction, because with lexicals you are
guaranteed that no such infection can occur.  (Unless you explicitly
want it via define_method.)

>> class Klass
>> � GLOBALS.x
>> � def bar
>> � � GLOBALS.x
>> � end
>> end
>>
>> This makes your intention explicit. �If you know python, you've probably
>> heard the phrase, "Explicit is better than implicit." �:)
>
> Except I do NOT want x to be globally accessible. I just want to to be
> accessible be child scopes. If I wanted it to be globally accessible,
> then I would just use global variables.

So don't make it global, as in the Top example above.
Ara H. (Guest)
on 2008-11-01 07:42
(Received via mailing list)
On Oct 31, 2008, at 3:08 PM, Yuh-Ruey C. wrote:

> But something like:
>
> x = 10
> def foo
>   scope.x
> end
>
> would not be as fragile, assuming |scope| is a new builtin method that
> does what you'd expect it do.

you can write it yourself now


cfp:~ > cat a.rb
class C
   include(
      Module.new do
        y = 42
        def x() y end

       define_method :method_missing do |_|
         if((_ = local_variables.index(_.to_s)))
           eval local_variables[_]
         else
           super
         end
       end
     end
   )
end

p C.new.x



cfp:~ > ruby a.rb
42


i might add, however, that a function that has access to non-local
state it generally called 'a method on an object with instance
variables'  and ruby has several methods to accomplish this .  one
very simple method is this


cfp:~ > cat a.rb
object = Module.new do
   @x = 42

   def y() @x end

   extend self
end


p object.y

cfp:~ > ruby a.rb
42


and of course there are many more.

regards.

a @ http://codeforpeople.com/
Yuh-Ruey C. (Guest)
on 2008-11-01 10:00
(Received via mailing list)
Replying to multiple posts:

On Oct 31, 5:19 pm, Stefan L. <removed_email_address@domain.invalid>
wrote:
>     class Foo
>     p Foo.instance_methods(false)
> A def inside a method definition defines a method in
> self's class. It is not local to the enclosing method definition.

Wow, I did not realize that. That changes my thinking a lot.

>
> > Then why the first-class code blocks? Certainly makes Ruby a
> > functional language, if only for blocks.
>
> Yes, Ruby has a good deal of functional features and they
> fit in very nicely with the current scoping rules.
>
> Although I'm not sure what you mean with "first class" code
> blocks. The block syntax is actually tied to message passing.

Well, blocks aren't first class per-se, but they can easily be turned
into first-class procs.

>
> and module instance variables suffice. OTOH, you can nest
> blocks as much as you like and scoping is what you expect.

I recognize that access to nonlocal vars can be simulated with
instance variables. It does seem weird to allow two different
paradigms that interact with each other in a confusing - or at the
least non-trivial - matter (just look at my confusion).

I guess my big beef with Ruby is that it is too complex and bloated
with features that often are inconsistent or lack symmetry with each
other. It's too "magical", making it hard to learn exactly how the
language works. From a language design perspective, Ruby really seems
like a mess to me, although with moments of brilliance. Most of the
time, when I'm metaprogramming, I'm guessing and looking at the
output, hoping I got so-and-so quirk correct.

Actually, I think this all could be mitigated to a certain extent if
the documentation for Ruby were improved. I could not find an official
language reference besides the API documentation, which is not
sufficient by itself. Obviously it did not contain much information on
scoping or non-API language features.

On Oct 31, 5:43 pm, Mike G. <removed_email_address@domain.invalid> wrote:
> > define_method| besides this block issue. Compare:
>
> The break in symmetry is intentional because it prevents local variables
> from willy-nilly infecting your methods, which you agreed is a good
> thing.

I agreed in the sense that without extra syntax, it would be fragile,
but as my example pointed out, it doesn't necessarily have to be
fragile.

> > ruin it.
>
> But then you've missed out on the advantage of lexical local variables.
> What you propose takes the "lexical" out of "lexical scope".  It is just
> nested variables which are visible from other scopes.

No, what I am proposing is a mechanism for lexical scope. This is what
I mean with that |scope.x| - treat x as if it were from an enclosing
scope.

> > Except I do NOT want x to be globally accessible. I just want to to be
> > accessible be child scopes. If I wanted it to be globally accessible,
> > then I would just use global variables.
>
> So don't make it global, as in the Top example above.

But they are not local variables. Local vars for a function are
created each time the function is called. Having access to those local
vars outside of the function makes no sense at all.

On Nov 1, 12:40 am, "ara.t.howard" <removed_email_address@domain.invalid> wrote:
> > does what you'd expect it do.
>        define_method :method_missing do |_|
> p C.new.x
>
> cfp:~ > ruby a.rb
> 42

That's, um, interesting. Less efficient than I hoped, but then again
Ruby is not known for its efficiency. Though that method_missing does
give me an idea...
Mike G. (Guest)
on 2008-11-01 12:04
Yuh-Ruey C. wrote:
>> But then you've missed out on the advantage of lexical local variables.
>> What you propose takes the "lexical" out of "lexical scope". �It is just
>> nested variables which are visible from other scopes.
>
> No, what I am proposing is a mechanism for lexical scope. This is what
> I mean with that |scope.x| - treat x as if it were from an enclosing
> scope.

I understand what you are proposing.  It cannot be called a mechanism
for lexical scope, because it's fundamentally inconsistent with the
term.  A 'def' method definition has a _different_ scope, not a nested
scope.  _By definition_, a scope cannot refer to lexical variables in a
different scope.  That's the whole point of lexical local variables.

There's a reason why Lisp moved to lexical scoping and never looked
back.  Ruby uses lexical scoping for the exact same reason.  It is
better.

Again, just make your shared data explicit, like the SHARED struct in
the previous example.  That's all.  Others will be glad you were upfront
about what is shared.  When debugging, it's valuable to know at a glance
what variables can possibly affect state.

Think of the alternative.  When tracking down a bug, under your proposal
you would have to check -- recursively! -- for scope.x references to
each and every variable which exists.

>
>> > Except I do NOT want x to be globally accessible. I just want to to be
>> > accessible be child scopes. If I wanted it to be globally accessible,
>> > then I would just use global variables.
>>
>> So don't make it global, as in the Top example above.
>
> But they are not local variables. Local vars for a function are
> created each time the function is called. Having access to those local
> vars outside of the function makes no sense at all.

This paragraph does not make sense to me.  In my example, the SHARED
OpenStruct instance is not a local variable.  It starts with a capital
letter.
Stefan L. (Guest)
on 2008-11-01 13:55
(Received via mailing list)
2008/11/1 Yuh-Ruey C. <removed_email_address@domain.invalid>:
>
> I recognize that access to nonlocal vars can be simulated with
> instance variables.

Instance variables don't simulate anything.

> It does seem weird to allow two different
> paradigms that interact with each other in a confusing - or at the
> least non-trivial - matter (just look at my confusion).

It seems to me you are confused because you are learning by
posting questions on mailing lists instead of following
some coherent book or introduction material.

> I guess my big beef with Ruby is that it is too complex and bloated
> with features that often are inconsistent or lack symmetry with each
> other. It's too "magical", making it hard to learn exactly how the
> language works. From a language design perspective, Ruby really seems
> like a mess to me, although with moments of brilliance. Most of the
> time, when I'm metaprogramming, I'm guessing and looking at the
> output, hoping I got so-and-so quirk correct.

Then stop guessing and learn first. Start with Programming Ruby,
here's an older version online:
http://ruby-doc.org/docs/ProgrammingRuby/

And search ruby-doc.org for more material. Seriously, you sound
like you're parroting some preconceived views about Ruby I've
seen before from other Python programmers, without even trying
to learn.

Stefan
Brian C. (Guest)
on 2008-11-01 13:56
There is a another reason why class and def create fresh scopes, related
to the way Ruby resolves method / local variable ambiguity.

One of the benefits of Ruby is its lack of verbosity: in particular no
need to declare variables before using them, and no need to put '()'
after a method call if it doesn't have any arguments. The result is
compact and readable code.

However it leaves an ambiguity: is a bareword like "x" to be interpreted
as a method call, i.e. self.x(), or as referring to a local variable?

This is resolved statically at parse time with a simple rule. If an
assignment expression "x = ..." has been seen earlier in the current
scope (regardless of whether it is actually executed), then a bareword
"x" is a local variable, otherwise it's a method call.

So:

  def x
    "hello"
  end

  def foo
    if false
      x = "bye"
    end
    puts x    # prints nil (local variable, no value assigned)
  end

This is unusual, but once you've used it, it makes perfect sense.

Now consider what would happen if 'def' didn't start a new scope.

  x = 123

  .... 1000 lines of code

  def x
    "hello"
  end

  def foo
    puts x    # 123 instead of hello?
  end

The code would behave differently, because of the binding to the 'x'
outside.

For me, the important thing is this: within the method body of 'foo', I
can tell for sure whether x is a local variable or a method call, by
inspecting *only* the code within that method. If it weren't for this
flushing of the scope, then I couldn't be sure without reading the
*whole* of the rest of the source file up to that point.

The same argument applies to a class, and class methods:

  # silly example
  memoize = 99

  ... 1000 lines ...

  class Foo
    extend Flurble
    memoize
  end

Just by inspecting the body between 'class Foo' and 'end', I can be sure
that memoize must be a method call - perhaps something in module
Flurble, or else some method already added to class Class. Without this
scope flushing, I would have to scan the whole of the rest of the source
file.

This behaviour may not sit comfortably with users of certain other
programming languages. I say that's fine. Ruby is a different language,
and has different idioms. If you are more comfortable with Lisp or
Haskell, then use those instead.

There are of course pros and cons to each way of doing things, but if
you open your mind to new ways of working, you may find the pros are
surprisingly substantial. (And I'm sure the same is true when moving in
the opposite direction too :-)
Robert D. (Guest)
on 2008-11-01 14:00
(Received via mailing list)
> 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.
Of course Python pays a (small) penalty for the lookup.

Closures might be more expensive though because of the local copies. I
however do not know
how this is optimized in the different VM.
Cheers
Robert
David A. Black (Guest)
on 2008-11-01 14:28
(Received via mailing list)
Hi --

On Sat, 1 Nov 2008, Yuh-Ruey C. wrote:

> least non-trivial - matter (just look at my confusion).
Everyone gets confused by something along the way in learning a
language. It can't really be used as a one-to-one measure of what's
wrong with the language. I think you're making this all much more
difficult for yourself than it needs to be, perhaps by starting from
the premise that it must be badly designed with no workable solutions.
You may well end up concluding that -- not everyone is destined to
love Ruby -- but I do think there's much more that you can let the
language tell you before you give up on it.

One concept to keep in mind is "self". Instance variables are pegged
to self; their visibility rule is that if self is x, then @var belongs
to x. There are no exceptions to that, and it doesn't interfere with
or really have anything to do with local variables.

> I guess my big beef with Ruby is that it is too complex and bloated
> with features that often are inconsistent or lack symmetry with each
> other. It's too "magical", making it hard to learn exactly how the
> language works. From a language design perspective, Ruby really seems
> like a mess to me, although with moments of brilliance. Most of the
> time, when I'm metaprogramming, I'm guessing and looking at the
> output, hoping I got so-and-so quirk correct.

I'd be interested in seeing some of these examples. In my experience,
a great deal of what people find confusing or opaque in Ruby turns out
to be confusing because it is so simple, and because there are so few
exceptions to the rules. I've always been impressed with the
strictness with which Ruby applies a small number of underlying
principles. It's possible that a bit of analysis of some examples
could help you nail some of those.


David
Yuh-Ruey C. (Guest)
on 2008-11-01 23:55
(Received via mailing list)
On Nov 1, 6:54 am, Stefan L. <removed_email_address@domain.invalid>
wrote:
> >> and module instance variables suffice. OTOH, you can nest
> >> blocks as much as you like and scoping is what you expect.
>
> > I recognize that access to nonlocal vars can be simulated with
> > instance variables.
>
> Instance variables don't simulate anything.

I mean that whatever you could do with access to nonlocal vars you
could do with a class with instance variables design. Just a different
programming methodology.

> > It does seem weird to allow two different
> > paradigms that interact with each other in a confusing - or at the
> > least non-trivial - matter (just look at my confusion).
>
> It seems to me you are confused because you are learning by
> posting questions on mailing lists instead of following
> some coherent book or introduction material.

I actually held off from posting here for a while during my
experimenting and reading of online docs.

>
> And search ruby-doc.org for more material. Seriously, you sound
> like you're parroting some preconceived views about Ruby I've
> seen before from other Python programmers, without even trying
> to learn.

Aside: I don't have any "preconceived" views on Ruby. I've heard about
how elegant it is, and indeed I found most of the syntax examples
elegant in some way or fashion. I'm also not primarily a Python
programmer, although I greatly admire how it integrates iteration so
well into the language. I work with all sorts of languages: C, Java,
Python, JS, or whatever else I find fascinating to experiment in.

I do feel that there are several aspects of Ruby that could be
simplified. It feels like Ruby has been influenced a lot by Perl, and
I absolutely loath Perl.

But there are several aspects of Ruby that I do admire: everything
being an object, open classes, how the block syntax meshes with a
function call, the various function calling sugar like omitting
parenthesis (although I think I slightly prefer Python's sugar with
regards to * and **).
Yuh-Ruey C. (Guest)
on 2008-11-02 00:11
(Received via mailing list)
On Nov 1, 5:03 am, Mike G. <removed_email_address@domain.invalid> wrote:
> for lexical scope, because it's fundamentally inconsistent with the
> term.  A 'def' method definition has a _different_ scope, not a nested
> scope.  _By definition_, a scope cannot refer to lexical variables in a
> different scope.  That's the whole point of lexical local variables.
>
> There's a reason why Lisp moved to lexical scoping and never looked
> back.  Ruby uses lexical scoping for the exact same reason.  It is
> better.

I'm not following you. Ruby uses lexical scope? AFAIK, it only does
that constants and not local variables.

My point is that if |def| were to use lexical scope with regards to
local variables, then there needs to be some sort of mechanism to
access those local variables. It could be:

scope.x

or

hey_ruby_interpreter__the_following_identifiers_are_not_methods__they_are_local_variables_from_parent_scopes
x

> Again, just make your shared data explicit, like the SHARED struct in
> the previous example.  That's all.  Others will be glad you were upfront
> about what is shared.  When debugging, it's valuable to know at a glance
> what variables can possibly affect state.
>
> Think of the alternative.  When tracking down a bug, under your proposal
> you would have to check -- recursively! -- for scope.x references to
> each and every variable which exists.

Nope, you're clearly not understanding my proposal. |def| currently
introduces a new empty scope. I'm saying that if |def| were changed so
that when first read by the interpreter, it looks at all the local
variables that could be accessed lexically if |def| had lexical scope,
and that within the |def|, there would be a mechanism for accessing
those local variables.

> This paragraph does not make sense to me.  In my example, the SHARED
> OpenStruct instance is not a local variable.  It starts with a capital
> letter.

Yes, I'm aware that it's not a local variable. I'm saying that using
constants like that cannot adequately simulate the semantics and
restrictions of local variables.
Yuh-Ruey C. (Guest)
on 2008-11-02 00:15
(Received via mailing list)
On Nov 1, 6:55 am, Brian C. <removed_email_address@domain.invalid> wrote:
>
>
>
>   end
> The same argument applies to a class, and class methods:
>
>
> There are of course pros and cons to each way of doing things, but if
> you open your mind to new ways of working, you may find the pros are
> surprisingly substantial. (And I'm sure the same is true when moving in
> the opposite direction too :-)

Brian, I am very aware of this issue. This is exactly why I didn't
propose that identifiers by themselves refer to local vars in
enclosing scopes. It would be incredibly fragile. To use my |scope.x|
syntax example with yours:

x = "hi"

def x
  "hello"
end

def foo
  if false
    x = "bye"
  end
  puts x    # prints nil (local variable, no value assigned)
  puts self.send(:x) # prints hello
  puts scope.x # prints hi
end
Ara H. (Guest)
on 2008-11-02 00:28
(Received via mailing list)
On Nov 1, 2008, at 4:08 PM, Yuh-Ruey C. wrote:

> I'm not following you. Ruby uses lexical scope? AFAIK, it only does
> that constants and not local variables.


if you are having a hard time doing something perhaps you could show
it?  one thing ruby does well is get things done with a happy medium
of abstraction.  unless something stands in the way of getting things
done simply it's sort of a non-issue don't you think?

cheers.

a @ http://codeforpeople.com/
Yuh-Ruey C. (Guest)
on 2008-11-02 00:40
(Received via mailing list)
On Nov 1, 7:26 am, "David A. Black" <removed_email_address@domain.invalid> 
wrote:
> to be confusing because it is so simple, and because there are so few
> exceptions to the rules. I've always been impressed with the
> strictness with which Ruby applies a small number of underlying
> principles. It's possible that a bit of analysis of some examples
> could help you nail some of those.

Okay, there are several little quirks of Ruby that annoy me, but I'll
just a list a couple:

1) When I saw the method! notation for mutation methods, I thought
that was particularly brilliant. But then I noticed that methods are
only postfixed with ! if there was already another method that did the
same thing except on a new copy. It would've been nice of all mutation
methods were postfixed with !. One use case for this would be trapping
all mutation methods of a class for some purpose (e.g. making a read-
only class, or a class that notifies an observer upon mutation).

2) Awkwardness of defining singleton methods. Contrast:

class << obj
  def singleton_method
    'foo'
  end
end

with:

obj.singleton_method = function() {
  return 'blah'
}

Of course, YMMV with regards to "elegance".

3) There are public/protected/private modifiers for functions, but not
for modules. (Yes, I'm aware that Ruby isn't Java, and that "private"
means "instance-private" not "class-private".)

4) "Scoping" weirdness (or whatever it's called) with modules and
classes.

class A
  class << self
    attr_accessor :x

    def foo
      @x = 20
    end
  end
end

A.foo
p A.x

B = Class.new
B.instance_eval do
  attr_accessor :x

  def foo
    @x = 40
  end
end

B.foo
p B.x  # error because self within B.instance_eval is still B, not the
singleton thingamajig. Yet by some magic, |def foo| makes a singleton
method.

So there's really another type of "scope" or whatever it's called
here: module vs. instance. And I have to remember not to trip up on
it.

Anyway, I can list other Ruby gripes, but this is all getting off
topic.
Yuh-Ruey C. (Guest)
on 2008-11-02 01:07
(Received via mailing list)
(Newgroup apparently ate my reply, but if it didn't, sorry for the
double post)

On Nov 1, 7:26 am, "David A. Black" <removed_email_address@domain.invalid> 
wrote:
> to be confusing because it is so simple, and because there are so few
> exceptions to the rules. I've always been impressed with the
> strictness with which Ruby applies a small number of underlying
> principles. It's possible that a bit of analysis of some examples
> could help you nail some of those.

Okay, there are several little quirks of Ruby that annoy me, but I'll
just a list a couple:

1) When I saw the method! notation for mutation methods, I thought
that was particularly brilliant. But then I noticed that methods are
only postfixed with ! if there was already another method that did the
same thing except on a new copy. It would've been nice of all mutation
methods were postfixed with !. One use case for this would be trapping
all mutation methods of a class for some purpose (e.g. making a read-
only class, or a class that notifies an observer upon mutation).

2) Awkwardness of defining singleton methods. Contrast:

class << obj
  def singleton_method
    'foo'
  end
end

with:

obj.singleton_method = function() {
  return 'blah'
}

Of course, YMMV with regards to "elegance".

3) There are public/protected/private modifiers for functions, but not
for modules. (Yes, I'm aware that Ruby isn't Java, and that "private"
means "instance-private" not "class-private".)

4) "Scoping" weirdness (or whatever it's called) with modules and
classes.

class A
  class << self
    attr_accessor :x

    def foo
      @x = 20
    end
  end
end

A.foo
p A.x

B = Class.new
B.instance_eval do
  attr_accessor :x

  def foo
    @x = 40
  end
end

B.foo
p B.x  # error because self within B.instance_eval is still B, not the
singleton thingamajig. Yet by some magic, |def foo| makes a singleton
method.

So there's really another type of "scope" or whatever it's called
here: module vs. instance. And I have to remember not to trip up on
it.

Anyway, I can list other Ruby gripes, but this is all off topic.
Yuh-Ruey C. (Guest)
on 2008-11-02 01:10
(Received via mailing list)
On Nov 1, 5:26 pm, "ara.t.howard" <removed_email_address@domain.invalid> wrote:
> cheers.
>
> a @http://codeforpeople.com/
> --
> we can deny everything, except that we have the possibility of being
> better. simply reflect on that.
> h.h. the 14th dalai lama

I have already posted an example:

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

Of course, afterwards I found out that Ruby doesn't really have nested
procedures, even if it tricks you into thinking that has them. So it's
kinda a moot point now.
Gary W. (Guest)
on 2008-11-02 02:26
(Received via mailing list)
On Nov 1, 2008, at 6:38 PM, Yuh-Ruey C. wrote:
> Okay, there are several little quirks of Ruby that annoy me, but I'll
> just a list a couple:
>
> 1) When I saw the method! notation for mutation methods, I thought
> that was particularly brilliant.

The method! notation is not a notation for mutation methods.  That
particular syntax is strictly a stylistic convention.  It has no
inherent semantics from the language perspective.  The convention
is that the bang method is more 'dangerous' than the 'regular'
non-bang method.  The meaning of 'dangerous' is completely relative
to the the method and class context.  Sometimes it means that the
object will be mutated but sometimes it means something else. Since
the convention is based on comparing two methods (m and m!) it breaks
the convention to define m! without also defining m.

> obj.singleton_method = function() {
>  return 'blah'
> }

Try:

def obj.singleton_method
   'foo'
end
Ara H. (Guest)
on 2008-11-02 03:10
(Received via mailing list)
On Nov 1, 2008, at 5:08 PM, Yuh-Ruey C. wrote:

> # here, we should not be able to access foo's a, but if there is
> another a in scope, we can access that
>
> Of course, afterwards I found out that Ruby doesn't really have nested
> procedures, even if it tricks you into thinking that has them. So it's
> kinda a moot point now.



it's not moot, it's easy and you *can* have nested procedures:



cfp:~ > cat a.rb
def foo
   a = 10
   this = self.class.method(:foo)
   this.singleton_class{ define_method(:bar){ a } }
   this.bar()
end


p foo()
p bar()


BEGIN {
   def singleton_class &b
     sc =
       class << self; self end
     b ? sc.module_eval(&b) : sc
   end
}




cfp:~ > ruby a.rb
10
a.rb:11: undefined method `bar' for main:Object (NoMethodError)



here 'bar' is a static method (attached to the method object *itself*)
of foo.  of course it has access to the local variables.  this
construct makes even methods which take blocks trivial


cfp:~ > cat a.rb
def foo &block
   a = 10
   this = self.class.method(:foo)
   this.singleton_class{ define_method(:bar){ block.call + a } }
   this.bar()
end


p foo{ 32 }


BEGIN {
   def singleton_class &b
     sc =
       class << self; self end
     b ? sc.module_eval(&b) : sc
   end
}




cfp:~ > ruby a.rb
42




i'll accept that you might not like the syntax, but as someone who
coded perl professionally for 5 years i know exactly the pain that
'my' can inflict - here is one of the last perl scripts i wrote

   http://codeforpeople.com/lib/perl/ozone/ozone.pl

i save it to remind myself of the insanity you have to jump through to
provide data encapsulation, which 30 years of software design has
proven to be a 'good thing' (recall that 'my' is/was heavily
reccomended by all the perl style guids (at the time).  in any case,
we do the same with ml/lisp etc - only insane people don't use 'let'
and then only with good reason.  all of us who remember that recall
using a debugger to figure out where that damn variable came from (i
have never, in 7 years, needed a debugger with ruby).  (OT - i *did*
learn a ton about these concepts from http://www.manning.com/conway -
highly recommended for people that want to know what it is to *make*
objects from scratch using little else but scoping rules)

i totally see that you are correct, but it really seems like you are
arguing for something as provably arcane and slippery as GOTO - it
might make certain programs shorter, but it makes *all* programs more
difficult to maintain.

in the end it seems vastly simpler to *provide* a scope when it's
needed, rather than to always be wondering what the scope actually
is.  my brain hates stack frames....

the key to seeing how to do this is to realize blocks *always* cary
the scope/closure in ruby, you can always do this:

cfp:~ > cat a.rb
def foo &block
   b = block.call
   a = eval('a', block)
   a + b
end

a = 32
p foo{ 10 }


cfp:~ > ruby a.rb
42


and you can always use Kernel.binding, which ERB certainly does.


as matz says, "it's no harm to learn new programming languages".  or,
as a british guy i used to now used to say: "suck it and see"

good luck!




a @ http://codeforpeople.com/
David A. Black (Guest)
on 2008-11-02 06:55
(Received via mailing list)
Hi --

On Sun, 2 Nov 2008, Yuh-Ruey C. wrote:

>> a great deal of what people find confusing or opaque in Ruby turns out
> that was particularly brilliant. But then I noticed that methods are
> only postfixed with ! if there was already another method that did the
> same thing except on a new copy. It would've been nice of all mutation
> methods were postfixed with !. One use case for this would be trapping
> all mutation methods of a class for some purpose (e.g. making a read-
> only class, or a class that notifies an observer upon mutation).

The ! methods are not specifically mutation methods. See:
http://dablog.rubypal.com/2007/8/15/bang-methods-o...
for an explanation of what they signify (and why it's cool, and why
putting ! on Array#pop and so forth would not be cool).

> obj.singleton_method = function() {
>  return 'blah'
> }
>
> Of course, YMMV with regards to "elegance".

When writing instance methods, I like to be inside a code definition
block, without regard to whether the class is a singleton class or
not. If you don't, you can always do:

   def obj.some_method

but that gets tiresome if you've got a lot of them. class << obj just
puts you in the class block, and then you proceed as you always do
with instance methods. There's really no need for a whole separate
apparatus; the class/object model is already designed to give you the
interface to singleton behavior that you need.

>
> B.instance_eval do
> method.
It's by decree. It's extremely rare to use def inside a code block, so
the idea, as I understand it, is that this is a way to introduce the
ability to do the almost only imagineable such scenario: namely,
creating a singleton method. The other imagineable similar scenario
is represented by class_eval (aka module_eval).

It's also related to the fact that def is a keyword, not a method. In
your example, attr_accessor is simply a method call without an
explicit receiver, which is always sent to "self" (B, in this case).
There's no complexity there. def, however, doesn't have a receiver;
it's a different kind of construct. So decisions always have to be
made about what its effect will be in a given context. Its treatment
in instance_eval is definitely special-cased, but if it weren't (if
def just created an outer-level method, as it would in a
non-special-cased code block), it would never be used (just as def
never is in normal code blocks).

So it's just a bit of quasi-vacant semantic space with a possible
use-scenario, and it got engineered accordingly.

> So there's really another type of "scope" or whatever it's called
> here: module vs. instance. And I have to remember not to trip up on
> it.

In general you have to remember not to trip up on all the idioms of
any language :-) It sounds like you're disgruntled about Ruby
specifically because you're finding things that you have to learn, and
that you can't just slide into based on what you've done in other
languages. That's correct: Ruby really is its own language, and you do
have to learn its idioms and toolsets.

I would second what Ara said, namely that it would be interesting to
see some actual things you're trying to do, and points at which you
feel that Ruby's scoping rules and so forth are stopping you.


David
This topic is locked and can not be replied to.