3 ms·
This feels like a philosophical question at this point. If the default object Kernel is always the same, then isn't it a global read variable? You can overwrit
by TZubiri 2y ago
This feels like a philosophical question at this point.
If the default object Kernel is always the same, then isn't it a global read variable? You can overwrite it, but it's globally accessible.
Also, the only true 'global' variables, are ones that you can access via Internet Protocol, everything you do in ruby, if you are not doing inter process communication, is going to be englobed by the process object.
Even if you write a variable to a file, it's still not 'global', it's encapsulated by the (physical) server object.
By reading or writing to a server through TCP to an open port public IP, the variable is then encapsulated only by the Earth object, so it's then and only then truly 'global'
- weatherlight 2y agoits a take for sure, but it feels like it goes a bit off-topic. The OP article is about the idea that Ruby doesn’t really have global methods since methods in Kernel are scoped within each object. Arguing that only network-accessible variables are truly global posits more philosophical than practical, thus lacking utility for this context. Especially when some langs do indeed, in fact have global methods/state that's accessible from anywhere.
- semiquaver 2y agoNot at all. Consider the following: class Foo def puts(*) Kernel.send(:puts, "Not what you expected") end def initialize puts "hello" end end Foo.new #=> prints "Not what you expected" The point the article makes is that the “default object” (self) is not always the same.
- Dylan16807 2y ago> Consider the following: That looks like variable shadowing? "global" doesn't mean "can't be shadowed". > The point the article makes is that the “default object” (self) is not always the same. I don't understand this sentence. Of course self isn't always the same. What does [“default object” (self)] mean?
- int_19h 2y agoIt means that the default object (the one that receives the method call in the absence of a receiver) is not a global.
- Dylan16807 2y agoWon'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".
- ryandv 2y agoIn 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)`: Foo.ancestors.map { |a| [a, a.is_a?(Class) ? a.instance_methods.include?(:puts) : a.respond_to?(:puts)] } => [[Foo, true], [Object, false], [Kernel, true], [BasicObject, false]] We find that `Foo.instance_methods.include?(:puts)`, and so that is the method that ends up being invoked: Foo.instance_method(:puts).source_location => ["(irb)", 2] # line 2 of semiquaver's example Consider now the class Bar: class Bar def initialize puts "hello" end end ... and notice that it does not contain any instance method `puts`: Bar.instance_method(:puts).source_location => nil Thus, unlike in the case of Foo, we need to traverse `Bar.ancestors` until we find a suitable definition for the method `:puts`: Bar.ancestors.map { |a| [a, a.is_a?(Class) ? a.instance_methods.include?(:puts) : a.respond_to?(:puts)] } => [[Bar, false], [Object, false], [Kernel, true], [BasicObject, false]] 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.
- layer8 2y agoI don’t really know Ruby, but I imagine that if a subclass overrides puts, then code in the base class calling puts will invoke that override instead of invoking the pseudo-global Kernel version. In other languages, the method definition in the subclass would be irrelevant, calling the global function wouldn’t cause polymorphic method dispatch on the current object.
- Lammy 2y agoRuby has Refinements for a less-global way of doing this https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc.html https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc...
- PittleyDunkin 2y ago> This feels like a philosophical question at this point. Kind of, but a) not all questions of semantics make for interesting philosophy, and b) the semantics in this case absolutely map to meaningful concerns regarding performance, behavior, and debugability/tactics for inspection of runtime environments. The fact that ruby doesn't have the concept of a function without a parent object whose method table is referenced by the caller complicates (for instance) passing around functions as values as well as whether or not you can expect the same function to even behave the same way when calling it twice. Of course, much of this is hidden by syntax, and it certainly doesn't restrict you from acting as if it has proper functions rather than just methods, but it absolutely does matter if you want to understand how your code is running, particularly details like memory usage and garbage collection and whether or not ruby can inline code (spoiler: it generally can't).
- 0x457 2y agoI think example about `avatar_url` shows that it isn't a philosophical question.