-6

Can you spot the problem in the following code?

module F
  def f(x)
    return x*x
  end
end

class Test3
  include F
  def self.ok(x)
    return f(2*x)
  end
end

How about this one?

class Test1
   def f(x)
     return x*x
   end
   def Test1.ok(x)
     return f(2*x)
   end
end

Or maybe even this?

class Test2
   def self.ok(x)
     def f(x)
       return x*x
     end
     return f(2*x)
   end
end

This is not intuitive at all. Why can't Ruby find 'f' in any of these cases?

David M. Rogers
  • 383
  • 3
  • 11

2 Answers2

4

Like many object-oriented languages, Ruby has separation between methods in the class context and those in the instance context:

class Example
  def self.a_class_method
    "I'm a class method!"
  end

  def an_instance_method
    "I'm an instance method!"
  end
end

When calling them using their native context it works:

Example.a_class_method
Example.new.an_instance_method

When calling them in the wrong context you get errors:

Example.new.a_class_method # => No such method
Example.an_instance_method # => No such method

Since an instance is related to a class you can do this:

Example.new.class.a_class_method # => Works!

That's often done inside instance methods that need to use class methods. Class methods aren't really able to utilize instance methods except if there's an instance in play. When deciding to make a method, you often need to consider the context in which it will be used. If it's instance related, especially if it uses @-style instance variables, then it's an instance method. If it's a generic utility method that is intended to be used independent of any particular instance, it's a class method.

In other languages the class methods are called "static" methods, they're stand-alone basically, but in Ruby they actually relate to the class itself, which is a special object, so it can be a little confusing at times.

That's why paying attention to the presence of self in the definition is very important.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • With that degree of separation, I'm now wondering why anyone would use a class method instead of writing a module to hold all those static functions. – David M. Rogers Apr 07 '17 at 08:57
  • A class is also a module, so sometimes it's easier to organize things within the class itself. For example: `Company.count` and `Company.new.name` from ActiveRecord. Another is how`Regexp.escape` works on arbitrary input, it's a utility function, but `Regexp.new(...).match(...)` is an instance method that only makes sense with a regular expression for context. – tadman Apr 07 '17 at 09:02
  • One of the things that makes Ruby quite powerful is how expressive you can be, how with the right care to name and organize things the code itself communicates intent quite well. Putting things under layers of abstraction muddles meaning. `Company::DataStore.current.count_instances` is a step in the opposite direction, even if functionally it did the same thing. – tadman Apr 07 '17 at 09:04
  • "Like many object-oriented languages, Ruby has separation between methods in the class context and those in the instance context" – Actually, Ruby is pretty unique in that it does *not* have different kinds of methods. All methods are instance methods. The question is just: *which* class are they defined in? – Jörg W Mittag Apr 07 '17 at 09:26
  • 1
    @JörgWMittag I'm over-simplifying here to try and clarify. They're technically class *instance* variables, but that's just plain bizarre to anyone who doesn't know the whole deal. I did try and hint at that near the end. – tadman Apr 07 '17 at 09:27
  • Personally, I am not convinced that simplified explanations of the Ruby object model, or Ruby message dispatch are helpful. Because, when it comes down to it, it *already* is pretty simple, and many simplified explanations are actually more complex than the real thing. For example, explanations that try to draw analogies to Java and talk about static methods or class methods or constructors. Well, none of those exist in Ruby, there is exactly one kind of method: instance methods. And message dispatch is equally simple: look up the class pointer, then follow the superclass pointers. `include` … – Jörg W Mittag Apr 07 '17 at 22:03
  • … is simple: it just makes the module the superclass of the class it is included into. Instance variables are simple, they always are looked up on `self`. All of those tend to be explained with complex analogies, or simplified but actually complex paraphrasing of the actual algorithms involved. I prefer to just explain it like it is. Even though Ruby *is* a rather complex language, its core is less complex than it is often made out to be. – Jörg W Mittag Apr 07 '17 at 22:05
  • @JörgWMittag It's simple *when* you understand it, but there are conceptual barriers there, especially if you're tainted by your understanding of Java, C++ or C# where there are certain things you've come to accept as fundamental principles. When those do not apply in Ruby it can be highly disorienting, so I'm just trying to build bridges here through a series of incremental steps. – tadman Apr 07 '17 at 22:07
-2

Because we can't get to f directly from the class method, there are a few options that make sense. First, just demote 'f'.

class Test1
   def self.f(x)
     return x*x
   end
   def self.ok(x)
     return f(2*x)
   end
end

Next is the most troubling case. Apparently, functions have a different scope than variables even when defined on the very same line. Here, defining f as a variable, we can access it correctly. Most languages have support for closures. Ruby has blocks. However, it can never have thunks without arcane syntax because of its no-parentheses calling convention.

class Test2
   def self.ok(x)
     f = lambda {|x| x*x}
     return f.call(2*x)
   end
end

module F
  def f(x)
    return x*x
  end
end

Finally, the include can be demoted in scope. Double-includes anyone?

class Test3
  class << self
    include F
  end
  def self.ok(x)
    return f(2*x)
  end
end
David M. Rogers
  • 383
  • 3
  • 11