Remix.run Logo
chao- 11 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.