In Ruby, the method puts is a singleton method of the Kernel module.
Normally, when a module is included or extended by another module, the module (but not its singleton class) is added to the inheritance tree. That effectively makes the instance methods of the module available to either the module or its singleton class (for include and extend, respectively)... but the singleton methods of a mixed-in module remain inaccessible, because the singleton class of a module isn't ever added to the inheritance tree.
So why can I use puts (and other Kernel singleton methods)?
Kernel.singleton_methods(false)
# => [:caller_locations, :local_variables, :require, :require_relative, :autoload, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :test, :warn, :autoload?, :fork, :binding, :exit, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :URI, :eval, :iterator?, :block_given?, :catch, :throw, :loop, :gets, :sleep, :proc, :lambda, :trace_var, :untrace_var, :at_exit, :load, :Rational, :select, :Complex, :syscall, :open, :printf, :print, :putc, :puts, :readline, :readlines, :`, :p, :system, :spawn, :exec, :exit!, :abort, :set_trace_func, :rand, :srand, :trap, :caller]
Note that puts does not seem to be an instance method on Kernel:
Kernel.instance_methods.grep(/puts/)
# []
Although Object does include Kernel
Object.included_modules
# [Kernel]
as far as I can tell, Kernel's singleton class (#<Class:Kernel>) doesn't show up in the ancestors of any object. is_a? appears to agree with this:
Object.is_a? Class.singleton_class # false
Object.is_a? Kernel.singleton_class # false
Object.singleton_class.is_a? Class.singleton_class # true
Object.singleton_class.is_a? Kernel.singleton_class # false
Yet, for some reason, they show up as private methods for every object.
Object.puts "asdf"
# NoMethodError (private method `puts' called for Object:Class)
How does the method lookup find these methods at all if #<Class:Kernel> doesn't show up in the ancestor chain?
Related:
- Ruby method lookup path for an object
- Class, Module, their eigenclasses, and method lookup
- Note: this is different from what I'm asking, because this is class inheritance, so
#<Class:Class>inherits from#<Class:Module>
- Note: this is different from what I'm asking, because this is class inheritance, so
- Why a module's singleton method is not visible in downstream eigenclasses where it gets mixed?
You are looking in the wrong place.
Methods like
Kernel#Array,Kernel#Complex,Kernel#Float,Kernel#Hash,Kernel#Integer,Kernel#Rational,Kernel#String,Kernel#__callee__,Kernel#__dir__,Kernel#__method__,Kernel#`,Kernel#abort,Kernel#at_exit,Kernel#autoload,Kernel#autoload?,Kernel#binding,Kernel#block_given?,Kernel#callcc,Kernel#caller,Kernel#caller_locations,Kernel#catch,Kernel#eval,Kernel#exec,Kernel#exit,Kernel#exit!,Kernel#fail,Kernel#fork,Kernel#format,Kernel#gets,Kernel#global_variables,Kernel#initialize_clone,Kernel#initialize_copy,Kernel#initialize_dup,Kernel#iterator?,Kernel#lambda,Kernel#load,Kernel#local_variables,Kernel#loop,Kernel#open,Kernel#p,Kernel#pp,Kernel#print,Kernel#printf,Kernel#proc,Kernel#putc,Kernel#puts,Kernel#raise,Kernel#rand,Kernel#readline,Kernel#readlines,Kernel#require,Kernel#require_relative,Kernel#select,Kernel#set_trace_func,Kernel#sleep,Kernel#spawn,Kernel#sprintf,Kernel#srand,Kernel#syscall,Kernel#system,Kernel#test,Kernel#throw,Kernel#trace_var,Kernel#trap,Kernel#untrace_var, andKernel#warndon't do anything useful with their receiver. They don't call private methods, they don't access instance variables, they in fact completely ignore whatselfis.Therefore, it would be misleading if you call them like this:
Because a reader would be mislead into thinking that
putsdoes something withfoo, when in fact, it completely ignores it. (This applies especially to the printing family of methods, because there also existIO#putsand friends, which indeed do care about their receiver.)So, in order to prevent you from misleadingly calling these methods with a receiver, they are made
private, which means they can only be called without an explicit receiver. (Obviously, they will still be called onself, but at least that won't be so obvious visually.)Technically, these aren't really methods at all, they behave more like procedures, but Ruby doesn't have procedures, so this is the best way to "fake" them.
The reason why they are also defined as singleton methods is so that you can still call them in contexts where
Kernelis not in the inheritance hierarchy, e.g. something like this:And the reason why they need to be defined separately at all is that the instance method versions are
private. If they weren't, then you would simply be able to callKernel.putsanyway, becauseObjectincludesKernelandKernelis an instance ofModulewhich is a subclass ofObject, thusKernelis an indirect instance of itself. However, the methods areprivateand thus you would get ainstead. Therefore, they need to be duplicated separately. There is actually a helper method that does that:
Module#module_function. (This is also used forMath, where you can either call e.g.Math.sqrt(4)orinclude Math; sqrt(4). In this case, you have the choice ofincludeingMathor not, whereasKernelis pre-included inObjectalways.)So, in summary: the methods are duplicated as
privateinstance methods ofKernelas well aspublicsingleton methods (which is really just instance methods ofKernel's singleton class). The reason they are defined asprivateinstance methods is so they cannot be called with an explicit receiver and are forced to look more like procedures. The reason they are duplicated as singleton methods ofKernelis so that they can be called with an explicit receiver as long as that explicit receiver isKernel, in contexts whereKernelis not available in the inheritance hierarchy.Check this out: