Comment by bashkiddie

Comment by bashkiddie 10 hours ago

11 replies

> everything is an object, always.

I beg to differ. What object does the method `puts` belong to? Why do you not call puts with its objects name?

Ruby has a concept of mixins (Golang interfaces), these are not objects. Neither is `puts`

chao- 10 hours ago

>Ruby has a concept of mixins (Golang interfaces), these are not objects.

Ruby "mixins" are the affordance of sharing instance methods from a module via a keyword. Modules are objects, and are instances of class Module:

  module ProvidesFooMixin
    def foo = "foo"
  end

  class Bar
    include ProvidesFooMixin
  end

  Bar.new.foo
  # => "foo"

  puts(ProvidesFooMixin.class)
  # => Module

  ProvidesFooMixin.object_id
  # => (some integer value)
>Neither is `puts`

Like all methods, `puts` is an object:

  method(:puts)
  # => #<Method: Object(Kernel)#puts(*)>

  method(:puts).class
  # => Method

  method(:puts).object_id
  # => (some integer value)
Here you see evidence of where `puts` comes from: Kernel#puts via Object, which I will now explain in detail.

>What object does the method `puts` belong to?

It belongs to the object you are calling it from within. You don't need to call `puts` with a receiver because it is an instance method, just like you don't need to call an instance method `foo` via `self.foo`. But you could choose to use a receiver, since the `puts` you know and love is just another instance method. You can try `self.puts` for yourself in some context!

Your classes (and their instances) inherit this `self.puts` instance method from the Object class, which includes the Kernel module, which provides `Kernel#puts`. So the only reason you can send it as a message without a receiver is because it is just another instance method (again, the same as calling instance method `#foo` without using `self.foo`).

Caveat: You can build an "alternate universe" object hierarchy by inheriting from BasicObject, and in your alternate universe, you can choose to not `include Kernel`, and you will see that instances of your new objects do not have access to `puts` in their instance scope.

WJW 9 hours ago

`puts` is just a method of the Kernel module: https://ruby-doc.org/3.4.1/Kernel.html#method-i-puts, just like `p` and many others. Kernel is included in the Object class that is the root of the class hierarchy, so its methods are available in every ruby object.

Mixins are just modules, which are objects, which you can call methods on. (Or rather, send messages to) You can easily verify this in irb calling a method on (for example) the Enumerable module:

    irb(main):001> Enumerable.class
    => Module
You are right that a module is not a class, and it is not possible to call `.new` on it. But the module itself is very much an object.
cortesoft 10 hours ago

Well, “puts” is a method defined in the Kernel module, which is included in the Object class, which means it is available in all contexts (since every object is a subclass of the ‘Object’ class). So, to answer your question, the puts method belongs to the Kernel module object, and the Kernel module object is an instance of a Module object.

kazinator 10 hours ago

Answerable by a few minutes of googling. Sort of:

puts is a method which has a class: the Method class:

  irb(main):001:0> method(:puts)
  => #<Method: main.puts>
  irb(main):002:0> method(:puts).class
  => Method
Everything being a confused muddle in Ruby, there is evidently some Kernel base class that is injected into every Object, and puts is a private method in that:

  irb(main):003:0> 3.puts()
  Traceback (most recent call last):
          2: from /usr/bin/irb:11:in `<main>'
          1: from (irb):3
  NoMethodError (private method `puts' called for 3:Integer)
The Method class of puts is a real class with methods and all:

  irb(main):004:0> method(:puts).class.methods
  => [:allocate, :superclass, :<=>, :<=, :>=, :==, :===, :autoload?, :autoload, :included_modules, :include?, :name, :ancestors, :attr, :attr_reader, :attr_writer, :attr_accessor, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :public_constant, :freeze, :inspect, :deprecate_constant, :private_constant, :const_missing, :singleton_class?, :prepend, :class_exec, :module_eval, :class_eval, :include, :<, :>, :remove_method, :undef_method, :alias_method, :protected_method_defined?, :module_exec, :method_defined?, :public_method_defined?, :to_s, :public_class_method, :public_instance_method, :define_method, :private_method_defined?, :private_class_method, :instance_method, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variables, :method, :public_method, :singleton_method, :define_singleton_method, :public_send, :extend, :to_enum, :enum_for, :pp, :=~, :!~, :eql?, :respond_to?, :object_id, :send, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :taint, :tainted?, :untrust, :untaint, :trust, :untrusted?, :methods, :frozen?, :protected_methods, :singleton_methods, :public_methods, :private_methods, :!, :equal?, :instance_eval, :instance_exec, :!=, :__send__, :__id__]
  • cortesoft 10 hours ago

    > Everything being a confused muddle in Ruby

    It really isn’t a confused muddle, the rules are very clear. Just because it doesn’t match what you expect from your other language experience doesn’t mean it isn’t clear.

    • pxc 8 hours ago

      I'm not a Rubyist really, but I started using it at work a few weeks ago for a very small script. I'm not allergic to RTFM, so I picked up the canonical reference book on Ruby and occasionally visit the official docs for the language. I agree: the structure is clear and straightforward and there's nothing difficult about learning it.

      It's also the opposite of magic; magic is when language features can't be described in terms of the language itself.

    • kazinator 5 hours ago

      Clear, like the glass in Liberace's candelabra.

jfabre 10 hours ago

Modules (mixins) are objects, classes are also objects (of type Class).

The Kernel module is included in the Object class, which means its methods are available to every Ruby object and can be accessed from virtually any scope, including the top-level (global) context where self is an instance of Object called "main."

[removed] 7 hours ago
[deleted]
pxc 9 hours ago

> I beg to differ. [...] Why do you not call puts with its objects name?

In the first place, I'd say what you're asking for goes beyond "everything is an object".

But I think your questions can be answered in a way that affirms that "everything is an object" in Ruby anyway.

> Why do you not call puts with its objects name?

Because it belongs to whatever object you're working in already; `puts` is identical to `self.puts`. And yes, you're always working in an object: https://bparanj.gitbooks.io/ruby-basics/content/chapter1.htm...

> What object does the method `puts` belong to?

As indicated above, it belongs to the object `self`. It gets added object via the mixing-in Kernel module into Object. Kernel is itself an instance of class Module: https://docs.ruby-lang.org/en/3.4/Module.html

The `puts` in Kernel delegates to `puts` from IO, which is likewise an instance method belonging to the object you can refer to by the name `$stdout`: https://docs.ruby-lang.org/en/3.4/IO.html

> Ruby has a concept of mixins [and], these are not objects.

Sure they are. Mixins themselves inherit from Module, and Modules are also objects (just like classes are).

Some highlights from Chapter 27 ("Library Reference: The Class Model") of the recent edition of the pickaxe book (emphasis mine):

> The Kernel module is included by class Object, so its methods are available in every Ruby object. One of the reasons for the Kernel module is to allow methods like `puts` and `gets` to be available everywhere and even to look like global commands. Kernel methods allow Ruby to still maintain an "everything is an object semantics".

and regarding mixins:

> The Module class is the class of any module you declare with the `module` keyword. Each module is an instance of class Module.

on Object:

> Object is the parent class of (almost) all classes in Ruby unless a class explicitly inherits from. BasicObject. [...] Object mixes in the Kernel module, making the built-in functions globally accessible.

tl;dr: mixins in Ruby are instances of class Module, and their methods end up bound to instances of class Object. Abstract module methods that don't belong to a concrete instance of some class that mixed in their module belong to the Object that is the module itself (the instance of class Module). (The same kind of thing is how class methods work.)