Here is some explanation:
From ruby docs.
A Module is a collection of methods and constants. The methods in a module may be instance methods or module methods. Instance methods appear as methods in a class when the module is included, module methods do not. Conversely, module methods may be called without creating an encapsulating object, while instance methods may not. (See Module#module_function.)
self.methodname inside a module creates a module method.
In this case when you invoke M::helper you actually doing M.helper when you look at it from a C++ developer's point of view. The receiver is Module object (an instance of ruby builtin type Module) in this case.
Other way to looking at this is to understand a receiver concept, every method call consists of a receiver and method name (+ optionally params and code block). Receiver can be a Module object, Class object or instance of a user defined class.
You can only invoke Module (or Class) methods on a Module (or Class) object. You can invoke any method (Module/Class/instance) on an instance.
If you want to invoke an instance method defined in a module then you must give it a receiver by including that module in some class and creating an instance of it.
So in this case another solution can be:
module MM
def helper(param)
puts "hello #{param}"
end
class ReceiverClass
include MM # add helper() to ReceiverClass
end
class C < Struct.new(:param)
def work
ReceiverClass.new.helper(param)
end
end
end
c = MM::C.new("world")
c.work