Won't it go through your classes in order until it hits the top level object? Or something like that?
I don't think you and semiquaver are using the term "default object" the same way TZubiri was.
Either way, the ability to override doesn't mean the original value wasn't there. That original value was accessible from anywhere, and that sounds like it's pretty "global".
In Ruby, all methods have a receiver. In the case where no receiver is explicitly specified, it is assumed to be `self` - thus line 7 of semiquaver's example desugars to `self.puts "hello"`. This is the "default object," referred to in Metaprogramming Ruby as the "implicit receiver."
Since `self.class == Foo` at line 7, we must check that `self.class` (which is Foo) either defines an instance method `:puts`, or that some Module included in its ancestor list can `respond_to?(:puts)`:
We find that Kernel responds to the method :puts, and so that will be the method invoked when we call `Bar.new`. We can patch Kernel to see what happens when we call Bar.new:
module Kernel
alias_method :orig_puts, :puts
def puts(*args)
orig_puts "Receiver #{self.inspect}"
orig_puts(*args)
end
end
Bar.new
Receiver #<Bar:0x000000012006eae8>
hello
=> #<Bar:0x000000012006eae8>
Notice that `self`, the implicit receiver or "default object," is in fact an instance of the class `Bar`, even though the actual method invoked after going up the `Bar.ancestors` chain is on Kernel.
Hence, there are no globals - every method is invoked on some `self`, explicit or implicit, and method lookup goes up the ancestor chain until you bottom out at Kernel or BasicObject. Note that Kernel is itself an Object.
This is still an oversimplification. I recommend reading Metaprogramming Ruby and playing around in a REPL after reading the docs on Object, Class, Module, and BasicObject.
For the sake of brevity, I'll skip an explanation for how I was interpreting that phrase. Instead, let me put it this way. The fact that "self" changes based on context is a very basic and obvious thing, and I'm pretty sure nobody suggested that "self" would always be the same thing.
In particular, the first post in this comment chain was talking about Kernel being the same from anywhere (unless overridden).
I would personally call any method that Kernel responds to a "global" method. Likewise for anything on BasicObject. I'm not worried about the receiver, I'm worried about access to the method.
> I would personally call any method that Kernel responds to a "global" method.
But it's not really "global," because it's not actually accessible "everywhere," and so it's a poor choice of terminology. From the BasicObject docs [0]:
BasicObject does not include Kernel (for methods like puts)
Illustrated below:
class Quux < BasicObject
def initialize
puts "hello"
end
end
Quux.new # NoMethodError (undefined method `puts' for #<Quux:0x0000000134989768>)
Better not to use shady terminology and to dive into the actual mechanisms and details behind how things actually work. Imprecise terminology leads to inaccurate mental models of what the system is actually doing.
Though maybe you could argue that methods on `BasicObject` are "global" in a way.
I presume this means that there are scenarios where Kernel doesn't exist. In scenarios where Kernel exists, it is global.
For example if running on an embedded system without a Kernel.
The fact that the kernel is globally accessible to all objects is not a criticism of the language, you can't make up purity where it doesn't exist, as much as we may desire our programs are not ethereal math incantations that exist on Plato's world of ideals, and sometimes they depend on the Kernel which is the same for the whole process, (Duh).
I didn't even get into CPU defined functions like addition, and assignment. How would you argue that + is not a global function? If you want to argue that it can be overloaded, recall that we speak of the default function in a read only capacity.