Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Ruby Quiz (A Trick Question)

Here is a little Ruby trivium for you.

Type this into IRB:

def foo
  def bar
    1
  end
end

foo.bar
=> 1

Is this some magical lightweight object creation syntax so you can do cool method chaining? Let’s try another example:

def foo
  def foo
    1
  end
end

foo
=> nil

foo.foo
=> 1

So far so good. But now, type:

foo
=> 1

WTF? Is this a defect in Ruby?? Post your responses in the comments.

(Warning: this is a trick question)

Comments
  1. Tomtt says:

    My guess is that running foo overloads itself so that next time you run foo it runs the new def instead which returns 1. Do I get I prize? :)

  2. Dave Smith says:

    Hint: What does invoking “bar” yield before and after invoking “foo.bar”?

  3. In the first context you also get:

    Array.bar #=> 1

    bar is just defined on Object. I don’t know what good foo is doing on the outside… or why it’s return value changes after the deeper call. defect? I dunno. Useful… doubt it.

    Ruby can be a little strange around the edges.

  4. Mark Wilden says:

    Here’s my go:

    The “outer” foo definition is a method that, when invoked, defines a method foo that returns 1. The definition itself returns nil, so calling foo the first time returns nil. But that replaces foo with another definition, so calling foo the second time returns 1.

    What I’m gathering from this is that methods defined at global scope can be called on nil, (or any object?)?

    ///ark

  5. Ah thanks Dave. bar doesn’t get defined until foo runs. It makes some of the more verbose metaprogramming idioms look like overkill. I wonder if you can attach the inner method to an object. I’ll have to try the whole thing in a class context sometime.

  6. Tomtt says:

    You can build a silly iterate-then-repeat-last-value function using this principle as well:

    def fat_because
      def fat_because
        def fat_because
          def fat_because
            "when she sits around the house, she SITS AROUND THE HOUSE!"
           end
          "she had to go to Sea World to get baptized"
        end
        "whenever she goes to the beach the tide comes in!"
      end
      "she wakes up in sections!"
    end
    
    5.times do
      puts "Yo mama so fat %s" % fat_because
    end
    

    Produces:

    Yo mama so fat she wakes up in sections!
    Yo mama so fat whenever she goes to the beach the tide comes in!
    Yo mama so fat she had to go to Sea World to get baptized
    Yo mama so fat when she sits around the house, she SITS AROUND THE HOUSE!
    Yo mama so fat when she sits around the house, she SITS AROUND THE HOUSE!
    
  7. Tim Connor says:

    With apologies to eminem: “And all ya fools act like ya forgot about self.”

    Ya, the foo methods just defines the bar method on self, when called, right? More verbosely it’s roughly (not saying this syntax is correct, I suspect it might choke in rb, but for illustrative purposes)

    def foo
      self.define_method :bar proc(1)
    end
    
  8. Tim Connor says:

    damn, missed a comma as well.

  9. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  10. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  11. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  12. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  13. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  14. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  15. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  16. Garry says:

    Running ‘foo’ the first time runs ‘def foo; 1 end’, which now defines ‘foo’ to return 1. Running ‘foo’ at this point, without even doing ‘foo.foo’ will return 1. But why ‘foo.foo’ then also returns 1 is still a mystery to me. In fact, ‘foo.foo.foo.foo.foo’, ad infinitum, will still return 1. Interesting :)

  17. Garry says:

    OH JESUS, sorry for the million posts, the “Submit” button wasn’t doing anything for a while and I button mashed it!

  18. szeryf says:

    Garry, that’s because foo is defined on every object, including object 1:Fixnum, so foo.foo.foo.foo is just method chain. Each .foo is called on an object, that was returned from previous invocation. There’s nothing magical in it. In fact you can chain this way every method from object, like nil? or to_s.

  19. Milan says:

    In irb,
    self.class => Object, so that’s why foo gets defined as an instance method of Object. When executed inside a class, it gets defined as an instance method for that class.

    Oh, and Garry’s multiple comments re: multiple function calls are sort of accidentally hilarious.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *