Russell
Russell

Reputation: 12439

Why is "hello" printed twice in this Ruby script?

I decided to experiment with defining methods inside methods in Ruby and wrote this:

def foo
  def bar
    puts "hello"
  end
  bar
end

This defined, I ran foo and "hello" was printed as I expected. However I then tried foo.bar - which printed out "hello" twice. Why?

Upvotes: 2

Views: 327

Answers (3)

oldergod
oldergod

Reputation: 15010

foo.bar equals foo().bar() so you call first foo which contains bar so it is executed once here, then bar is executed one more time. At the end bar is called twice.

def foo
  puts 'foo called'
  def bar
    puts 'bar called'
    puts "hello"
  end
  bar
end
foo.bar
#=> foo called
#=> bar called
#=> hello
#=> bar called
#=> hello

What I don't understand is this means the result of foo which is nil can call bar.

>> nil.bar
#=> hello

How is that possible ?

Edit: As explained in some answers, nested method are included in the class scope so weither bar is declared inside or outside foo does not make any difference.

Also Matz said that it may change from Ruby2.0

here is the example he wrote:

class Foo
  def foo
    def bar
      #omited
    end
    bar # can be called
  end

  def quux
    bar # cannot be called
  end
end

edit-Ruby2.0: It finally did not get implemented. So it remains the same.

Upvotes: 4

Joshua Cheek
Joshua Cheek

Reputation: 31756

In Ruby, there is a hidden variable that doesn't get much attention. I don't know what it's called, but I call it the current class. This variable is the target of def

When you go into foo, it doesn't change this variable, you're still in the same class. So when you define another method inside of foo, that method gets defined on the same class as foo was defined on.

In this case, you're defining foo on main, the toplevel object. Main's current class variable is set to object, so any methods you define here will be available inside any object.

Since the last thing you do is return the result of bar, and bar returns nil (b/c puts returns nil), then foo.bar is nil.bar. And since you defined bar on Object, even nil inherits this method.

The interesting part is that bar is public. Since foo is defined in main, and main sets its scope to private, I would have expected bar to also be private, but I guess that state must lost once you go to a different scope.

Upvotes: 2

kristianp
kristianp

Reputation: 5895

Did you mean to put class foo? It seems that calling foo.bar calls the function foo and then the function bar. Calling nil.bar prints out 'hello' too.

Upvotes: 1

Related Questions