In Ruby, the method puts
is a singleton method of the Kernel
module.
Normally, when a module is include
d or extend
ed 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#warn
don't do anything useful with their receiver. They don't call private methods, they don't access instance variables, they in fact completely ignore whatself
is.Therefore, it would be misleading if you call them like this:
Because a reader would be mislead into thinking that
puts
does something withfoo
, when in fact, it completely ignores it. (This applies especially to the printing family of methods, because there also existIO#puts
and 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
Kernel
is 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.puts
anyway, becauseObject
includesKernel
andKernel
is an instance ofModule
which is a subclass ofObject
, thusKernel
is an indirect instance of itself. However, the methods areprivate
and 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 ofinclude
ingMath
or not, whereasKernel
is pre-include
d inObject
always.)So, in summary: the methods are duplicated as
private
instance methods ofKernel
as well aspublic
singleton methods (which is really just instance methods ofKernel
's singleton class). The reason they are defined asprivate
instance 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 ofKernel
is so that they can be called with an explicit receiver as long as that explicit receiver isKernel
, in contexts whereKernel
is not available in the inheritance hierarchy.Check this out: