What *are* variables? Which are nil now?

Reading about reflection, ObjectSpace will give you the objects in
your system, possibly selected by Class. Fine. It won’t tell you
which variables are associated with them. You can trace on global
variables, you can look at instance variables, but how can you get
information about all variables? What is a variable, exactly, come
to that? It works like a sticky label so you can have two on the
same object, but are the variables themselve objects in ruby? Can I
twist ObjectSpace’s arm to tell me about them?

Why have I run into this now?

I’m getting
#<TypeError: failed to convert nil into Array>
raised by a program where
rescue => e
p e, caller
gives some line numbers with an each keyword, where the code looks like
raise “@b1 is nil” if @b1.nil
@b1.each { |bf|
so I know that @b1 is not nil when it gets here. The each method is
one I wrote, so I’d expect it to blow up in there, with an
associated line number.

Can I get ruby to tell me all things that point at the one Nil
object in the universe, at the present time?

This is with 1.6.8 and with 1.8.0

    Thank you,
    Hugh

raise “@b1 is nil” if @b1.nil
@b1.each { |bf|

You mean @b1.nil? – right?

irb(main):004:0> class B
irb(main):005:1> def foo
irb(main):006:2> @b1 =
irb(main):007:2> raise “@b1 is nil” if @b1.nil?
irb(main):008:2> @b1 = nil
irb(main):009:2> raise “@b1 is nil” if @b1.nil?
irb(main):010:2> @b1.each { |e| print “.” }
irb(main):011:2> end
irb(main):012:1> end
=> nil
irb(main):013:0> B.new.foo
RuntimeError: @b1 is nil
from (irb):9:in `foo’
from (irb):13
irb(main):014:0>

Is there a chance that @b1 is becoming nil between your test and the each
because the program is threaded?

As far as I know, there’s no way to know what variables (labels) are
referring to what values (objects).

-austin

···


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.09.10
* 16.50.48

Variables are not objects; it’s as simple as that. They are sticky
notes. You can get a list of local variables in any scope with
Kernel::local_variables. Then you can eval them to find out what they
are.

You can get a list of variables in other scopes as well. I have a
marvellous proof for this, which this margin is too small to contain.

Gavin

···

On Thursday, September 11, 2003, 4:00:10 AM, Hugh wrote:

Reading about reflection, ObjectSpace will give you the objects in
your system, possibly selected by Class. Fine. It won’t tell you
which variables are associated with them. You can trace on global
variables, you can look at instance variables, but how can you get
information about all variables? What is a variable, exactly, come
to that? It works like a sticky label so you can have two on the
same object, but are the variables themselve objects in ruby? Can I
twist ObjectSpace’s arm to tell me about them?

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309101847560.23363@neelix…

Reading about reflection, ObjectSpace will give you the objects in
your system, possibly selected by Class. Fine. It won’t tell you
which variables are associated with them. You can trace on global
variables, you can look at instance variables, but how can you get
information about all variables? What is a variable, exactly, come
to that? It works like a sticky label so you can have two on the
same object, but are the variables themselve objects in ruby? Can I
twist ObjectSpace’s arm to tell me about them?

Why have I run into this now?

I’m getting
#<TypeError: failed to convert nil into Array>
raised by a program where
rescue => e
p e, caller
gives some line numbers with an each keyword, where the code looks like
raise “@b1 is nil” if @b1.nil
@b1.each { |bf|
so I know that @b1 is not nil when it gets here. The each method is
one I wrote, so I’d expect it to blow up in there, with an
associated line number.

There are two issues with the line “raise “@b1 is nil” if @b1.nil?”:

  1. The test is inappropriate, what you really want is to ensure that the
    obj referred to by @b has method “each”. So these are better alternatives
    (but see issue 2):

raise “Wrong @b1” unless @b1.kind_of? Enumerable
raise “Wrong @b1” unless @b1.respond_to? :each

  1. Btw, generally it’s superfluous to include the explicite test and raise
    since if the instance referred to by @b does not have an “each” method the
    code will blow anyway with an NoMethodError. So omitting this test is
    shorter and cleaner.

Can I get ruby to tell me all things that point at the one Nil
object in the universe, at the present time?

Well, you could do this

unless @b1.respond_to? :each
p @b1 # prints info about @b1
puts @b1.class # print type

print other informative stuff

raise “No each”
end

or use the debugger.

But as I said before, generally this is not a too good idea.

Regards

robert

> raise “@b1 is nil” if @b1.nil

> @b1.each { |bf|

···

On Thu, 11 Sep 2003, Austin Ziegler wrote:

You mean @b1.nil? – right?

I think he’s looking for something more like this:

VariableSpace.each_pointing_to(nil) do |var|

Do something with “var”, which is probably some kind of

object-oriented representation of a variable in memory.

end

No way to do anything even close to this that I know of.

Chad

raise “@b1 is nil” if @b1.nil
@b1.each { |bf|

You mean @b1.nil? – right?

Yes, didn’t paste it because it was short!

=> nil
irb(main):013:0> B.new.foo
RuntimeError: @b1 is nil

which is a different message…

    from (irb):9:in `foo'
    from (irb):13

irb(main):014:0>

Is there a chance that @b1 is becoming nil between your test and the each
because the program is threaded?

No, single thread only.

As far as I know, there’s no way to know what variables (labels) are
referring to what values (objects).

I found out what caused it:
@dirs += entries.collect{|f| stuff_here(f)}.compact!

The bang method caused the blow up, but even defining

class Nil
def to_a
raise “nil.to_a() called”
end
end

did not trap it.
Where does the “#<”…“failed to convert nil to Array”…>
message come from? Somewhere untrappable?

-austin
* 16.50.48
Hugh

···

On Thu, 11 Sep 2003, Austin Ziegler wrote:

Variables are not objects; it’s as simple as that. They are sticky
notes. You can get a list of local variables in any scope with
Kernel::local_variables. Then you can eval them to find out what they

thanks, I missed that one.

are.

You can get a list of variables in other scopes as well. I have a
marvellous proof for this, which this margin is too small to contain.

I seem to be needing large margins for my errors, I’ll send you one!
:slight_smile: I’d like to know how to extract this info from Ruby. It might
not have helped in my case though, but would be generally good to
have.

Gavin

    Thank you,
    Hugh
···

On Thu, 11 Sep 2003, Gavin Sinclair wrote:

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309101847560.23363@neelix…

gives some line numbers with an each keyword, where the code looks like
raise “@b1 is nil” if @b1.nil
@b1.each { |bf|
so I know that @b1 is not nil when it gets here. The each method is
one I wrote, so I’d expect it to blow up in there, with an
associated line number.

There are two issues with the line “raise “@b1 is nil” if @b1.nil?”:

  1. The test is inappropriate, what you really want is to ensure that the

This was debugging code, added after getting the error. I wanted to
be sure that the @b1 was the thing that was becoming nil. It wasn’t

obj referred to by @b has method “each”. So these are better alternatives
(but see issue 2):

raise “Wrong @b1” unless @b1.kind_of? Enumerable
raise “Wrong @b1” unless @b1.respond_to? :each

Those would avoid an “undefined method” error, which was not what I
was getting. They are right for checking each though
I was getting “\n#<TypeError: failed to convert nil to Array>”

  1. Btw, generally it’s superfluous to include the explicite test and raise

Yes, it was not in the original program.

Can I get ruby to tell me all things that point at the one Nil
object in the universe, at the present time?

Well, you could do this

[ p @b1 # prints info about @b1 etc]

or use the debugger.

Yes, that produced the same message in some very odd places, but no
variables showed up with the error because it was in the middle of
an expression.

But as I said before, generally this is not a too good idea.

Regards

robert
    Thank you,
    Hugh
···

On Thu, 11 Sep 2003, Robert Klemme wrote:

[Hugh:]

Variables are not objects; it’s as simple as that. They are sticky
notes. You can get a list of local variables in any scope with
Kernel::local_variables. Then you can eval them to find out what they

thanks, I missed that one.

are.

You can get a list of variables in other scopes as well. I have a
marvellous proof for this, which this margin is too small to contain.

I seem to be needing large margins for my errors, I’ll send you one! :slight_smile:
I’d like to know how to extract this info from Ruby. It might not have
helped in my case though, but would be generally good to
have.

Heh, all I had in mind was this: if you can get a Binding to another
scope, then you can execute “local_variables” in that binding. To wit:

def foo
a = 1
b = 2
c = 3
return binding
end

b = foo # Binding:0x10187e70
local_variables # [“_”, “b”]
eval “local_variables”, b # [“a”, “b”, “c”]
eval “a”, b # 1
eval “b”, b # 2
eval “b” # Binding:0x10187e70

I don’t know if/how you can get a binding to another scope without
explicitly arranging it. Not through Kernel::caller, for better or worse.

Gavin

Now, who can tell me what that “_” local variable is? Extra points for

telling me why I should have known all along.

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309110023200.26201@neelix…

I found out what caused it:
@dirs += entries.collect{|f| stuff_here(f)}.compact!

The bang method caused the blow up, but even defining

class Nil
def to_a
raise “nil.to_a() called”
end
end

did not trap it.
Where does the “#<”…“failed to convert nil to Array”…>
message come from? Somewhere untrappable?

Since

@dirs += entries.collect{|f| stuff_here(f)}.compact!

<=>

@dirs = ( @dirs + entries.collect{|f| stuff_here(f)}.compact! )

Assuming that @dirs is an Array, Array.+ tries to convert the argument
(which is the result of the expr right to the “+”) to an Array and throws
the exception

Cheers

robert

> raise “@b1 is nil” if @b1.nil

> @b1.each { |bf|

You mean @b1.nil? – right?

Yes, I’d mistyped it.

I think he’s looking for something more like this:

VariableSpace.each_pointing_to(nil) do |var|

Do something with “var”, which is probably some kind of

object-oriented representation of a variable in memory.

end

Yes, though I suspected that this might not be possible: ruby itself
doesn’t do much with our ‘sticky note’ variable names because it
doesn’t need that sugar.

No way to do anything even close to this that I know of.

I suspected not.

Chad

    Thank you,
    Hugh
···

On Thu, 11 Sep 2003, Chad Fowler wrote:

On Thu, 11 Sep 2003, Austin Ziegler wrote:

Gavin Sinclair wrote:

Now, who can tell me what that “_” local variable is? Extra points for

telling me why I should have known all along.

Must be an irb thing. I don’t see it when executing your code in ruby
directly, only in irb.

You can get a list of variables in other scopes as well. I have a
marvellous proof for this, which this margin is too small to contain.
[…]
I’d like to know how to extract this info from Ruby. It might not have

Heh, all I had in mind was this: if you can get a Binding to another
scope, then you can execute “local_variables” in that binding. To wit:

Oh, nice! Yes, and I can roll that into the exception object I
throw.

    Thank you
    Hugh
···

On Thu, 11 Sep 2003, Gavin Sinclair wrote:

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309110023200.26201@neelix…

I found out what caused it:
@dirs += entries.collect{|f| stuff_here(f)}.compact!

The bang method caused the blow up, but even defining

    [Nil#to_a]

did not trap it.
Where does the “#<”…“failed to convert nil to Array”…>
message come from? Somewhere untrappable?

Since

@dirs += entries.collect{|f| stuff_here(f)}.compact!

<=>

@dirs = ( @dirs + entries.collect{|f| stuff_here(f)}.compact! )

Assuming that @dirs is an Array, Array.+ tries to convert the argument
(which is the result of the expr right to the “+”) to an Array and throws
the exception

Agreed, but is the point where the message is generated accessible,
such that I can interpose something? This Array#+ isn’t calling
arg.to_a as far as the above shows…

Cheers

robert
    Hugh
···

On Thu, 11 Sep 2003, Robert Klemme wrote:

Gavin Sinclair wrote:

Now, who can tell me what that “_” local variable is? Extra points

for # telling me why I should have known all along.

Must be an irb thing. I don’t see it when executing your code in ruby
directly, only in irb.

Ah, interesting. My instinct is to ask: “how can irb inject a local
variable into my local context?”

I think I’ve seen code where “_” is used as a parameter in a block,
signifying that we’re not interested in that value. For instance:

hash.map { |_, value| f(value) }

Gavin

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309111033140.26256@neelix…

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309110023200.26201@neelix…

I found out what caused it:
@dirs += entries.collect{|f| stuff_here(f)}.compact!

The bang method caused the blow up, but even defining

    [Nil#to_a]

did not trap it.
Where does the “#<”…“failed to convert nil to Array”…>
message come from? Somewhere untrappable?

Since

@dirs += entries.collect{|f| stuff_here(f)}.compact!

<=>

@dirs = ( @dirs + entries.collect{|f| stuff_here(f)}.compact! )

Assuming that @dirs is an Array, Array.+ tries to convert the argument
(which is the result of the expr right to the “+”) to an Array and
throws
the exception

Agreed, but is the point where the message is generated accessible,
such that I can interpose something? This Array#+ isn’t calling
arg.to_a as far as the above shows…

You can

  • redefine Array#+
  • look into the sources for Array#+

But isn’t the stack trace good enough for catching the error you got?

robert
···

On Thu, 11 Sep 2003, Robert Klemme wrote:

Agreed, but is the point where the message is generated accessible,
such that I can interpose something? This Array#+ isn't calling
arg.to_a as far as the above shows...

No, it call #to_ary

svg% ruby -e 'def nil.to_ary() [12] end; p +nil'
[12]
svg%

Guy Decoux

“Hugh Sasse Staff Elec Eng” hgs@dmu.ac.uk schrieb im Newsbeitrag
news:Pine.GSO.4.53.0309111033140.26256@neelix…

Agreed, but is the point where the message is generated accessible,
such that I can interpose something? This Array#+ isn’t calling
arg.to_a as far as the above shows…

You can

  • redefine Array#+

That occurred to me later. Originally I could not determine it was
that line though.

  • look into the sources for Array#+

Yes, I amy do that,

But isn’t the stack trace good enough for catching the error you got?

No: the first thing in the stack trace was that ‘@b1.each {|bf|’ line

robert
    thank you.
    Hugh
···

On Thu, 11 Sep 2003, Robert Klemme wrote:

Agreed, but is the point where the message is generated accessible,
such that I can interpose something? This Array#+ isn’t calling
arg.to_a as far as the above shows…

No, it call #to_ary

I can’t see that listed either in the windows copy of the Pickaxe,
or in nil,methods.

svg% ruby -e ‘def nil.to_ary() [12] end; p +nil’
[12]
svg%

so why doesn’t this give the TypeError then?

Guy Decoux

    Thank you,
    Hugh
···

On Thu, 11 Sep 2003, ts wrote:

svg% ruby -e 'def nil.to_ary() [12] end; p +nil'
[12]
svg%

so why doesn't this give the TypeError then?

Because, like I've said, Array#+ call #to_ary and because I've defined
nil#to_ary it just work

Guy Decoux