If `self` is always the implied receiver in Ruby, why doesn't `self.puts` work?
Private methods can't have a receiver
I think the answer is this: Ruby's way of enforcing method privacy is that it doesn't allow calling private methods with an explicit receiver.
An example:
class Baker
def bake_cake
make_batter
self.use_oven # will explode: called with explicit receiver 'self'
end
private
def make_batter
puts "making batter!"
end
def use_oven
puts "using oven!"
end
end
b = Baker.new
b.bake_cake
Since there can be no explicit receiver, you certainly can't do b.use_oven
. And that is how method privacy is enforced.
Why won't Ruby allow me to specify self as a receiver inside a private method?
The Problem
In Ruby, private methods can't be called directly with an explicit receiver; self doesn't get any special treatment here. By definition, when you call self.some_method
you are specifying self as the explicit receiver, so Ruby says "No!"
The Solution
Ruby has rules for its method lookups. There may be a more canonical source for the rules (other than going to the Ruby source), but this blog post lays out the rules right at the top:
1) Methods defined in the object’s singleton class (i.e. the object itself)
2) Modules mixed into the singleton class in reverse order of inclusion
3) Methods defined by the object’s class
4) Modules included into the object’s class in reverse order of inclusion
5) Methods defined by the object’s superclass, i.e. inherited methods
In other words, private methods are first looked up in self without requiring (or allowing) an explicit receiver.
Global methods in Ruby
For runtime Ruby has special object the main
.
It's some kind of trick that all code runs in the context of this object.
So when you're typing methods like puts
, p
and so on, all of them are calling in the context of self
object and passing to self
object.
And here's the second thing - the access control.
As you probably know, Ruby has keywords like private
, protected
and public
- all of them manage the access of calling methods on object. Ruby is checking this access control only when you're have construction like
<name_of_object>.your_method
and self.your_method
So when you're typing
self.p "something"
Ruby will decline this call because the p
method is private method.
Ruby's self vs. Python's self
Regarding Python I can tell you nothing new. The self
there is passed conventionally as the first parameter of a method, as pst said. From Python docs
Often, the first argument of a method is called self. This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a class browser program might be written that relies upon such a convention.
CRuby (or 'MRI') has something similar which happens under the hood though. Every C extension may define (module/class/singleton) methods on a Ruby class by using
- rb_define_method (instance)
- rb_define_singleton_method (singleton class)
- rb_define_module_function (class/module)
The actual implementing functions always take VALUE self
as their first argument, in analogy to the Python idiom. self
in these cases refers to the object instance this particular message has been sent to, i.e. if you have
person = Person.new
person.do_sth
and do_sth would happen to be implemented in C, then there would be a corresponding C function
VALUE
person_do_sth(VALUE self) {
//do something
return self;
}
Every such implementation has to return a VALUE
(the C representation of a Ruby object), which relates to the fact that every method call or message sent (to stick to Smalltalk parlance) has a return value in Ruby. There is no such thing as a void
function in Ruby.
Although we need to pass self
back and forth in the low-level C code, you don't need to do so in Ruby code, Ruby takes care of this for you. The current value of self is stored internally in the current thread context that is executed, so the existence of self
is granted, the message "self" will always evaluate to some object.
Due to the dynamic nature of Ruby, the actual value of of this object being referenced by self
changes with the current scope of the code that is currently interpreted. Run this to see for yourself:
puts "#{self} - declared in global scope" # the 'top self' aka 'main'
class << self
puts "#{self} - 'main's singleton class" # main's singleton or 'eigenclass'
end
puts "Starting to interpret class A code"
class A
puts "#{self} - When do I get executed!?" # self is class A
class << self
puts "#{self} - And me!?" # now A's singleton class
def a # declaring method in class's singleton class results in class method
puts "#{self} - declared in singleton class" # it's A
end
end
def self.b
puts "#{self} - declared in class method" # self is class A again -> class method
class << self
puts "#{self} - declared in Class A's singleton class" # now it's Class A's singleton class
end
end
def c
puts "#{self} - declared in instance method" # self is instance of A
class << self
puts "#{self} - declared in instance's singleton class" # now it's the A instance's singleton class
end
end
end
puts "All so far has happened simply by interpreting A's code"
a = A.new
A.a
A.b
a.c
If you want to call a method/send a message from any context to self
, you could do this either explicitly (e.g. self.method
) or you omit self
as the receiver - then, by convention, the implicit receiver of the message will be self
.
An interesting side note to this is Ruby's interpretation of private
methods, which differs e.g. from the Java notion of private
. Ruby's private methods are only callable by sending a message using self
as an implicit receiver, i.e.
class A
def a
b
end
private
def b
puts "I'm private"
end
end
a = A.new
a.a # => I'm private
works, whereas replacing method a by
def a
self.b
end
would raise an exception. This implies that something very common in Java
class A {
private boolean compareUs(A a1, A a2) { ... }
public boolean equals(A a1, A a2) {
return (a1.compareUs() == a2.compareUs());
}
}
won't work in Ruby. Silly example, but just to illustrate the point: In Java we can access private methods of other instances of the same class, this would not be possible in Ruby because we can only access private methods of the current self
.
Finally, to complicate things a bit further, the instance_eval
and class_eval
functions will also alter the value of self
during execution.
Why does Ruby have both private and protected methods?
protected
methods can be called by any instance of the defining class or its subclasses.
private
methods can be called only from within the calling object. You cannot access another instance's private methods directly.
Here is a quick practical example:
def compare_to(x)
self.some_method <=> x.some_method
end
some_method
cannot be private
here. It must be protected
because you need it to support explicit receivers. Your typical internal helper methods can usually be private
since they never need to be called like this.
It is important to note that this is different from the way Java or C++ works. private
in Ruby is similar to protected
in Java/C++ in that subclasses have access to the method. In Ruby, there is no way to restrict access to a method from its subclasses like you can with private
in Java.
Visibility in Ruby is largely a "recommendation" anyways since you can always gain access to a method using send
:
irb(main):001:0> class A
irb(main):002:1> private
irb(main):003:1> def not_so_private_method
irb(main):004:2> puts "Hello World"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> foo = A.new
=> #<A:0x31688f>
irb(main):009:0> foo.send :not_so_private_method
Hello World
=> nil
What is main in Ruby?
Everything in Ruby occurs in the context of some object. The object at the top level is called "main". It's basically an instance of Object with the special property that any methods defined there are added as instance methods of Object (so they're available everywhere).
So we can make a script consisting entirely of:
puts object_id
@a = 'Look, I have instance variables!'
puts @a
and it will print "105640" and "Look, I have instance variables!".
It's not something you generally need to concern yourself with, but it is there.
Dot syntax vs param passing syntax
Yes, you can add methods to the String class to achieve your desired effect; the variable "self
" refers to the object which receives the method call.
class String
def namechanger
"This is the name to change: #{self}"
end
end
"Steve".namechanger # => This is the name to change: Steve
This practice is known as monkey patching and should be used carefully.
Related Topics
How to Create an Operator for Deep Copy/Cloning of Objects in Ruby
Does Anyone Have Parsing Rules for the Notepad++ Function List Plugin for Ruby and Rake
Routing Error No Route Matches [Get] "/Static_Pages/Home", Tutorial
Advice on How to Validate Names and Surnames Using Regex
Ruby or Rails Sort on Two/Multiple Date Fields
Ruby/Rails Array of Strings to Postgresql Insert
Is There a Groovy Equivalent of the Ruby Timeout Module
Ruby Is Already Using the Class Name of My Model
Rails 7 Signup Form Doesn't Show Error Messages
Insert Rows on Specific Line in a File
How to Make a Ruby Script Using Trollop for Command Line Parsing
Compass Error for Command 'Grunt Server'
Rails - Multi Tenant Application with Customization Framework
Rails 4 Error with Every Command "'Load': No Implicit Conversion of Nil into String" (MAC Os X 10.9)
Ruby: How to Convert Ip Range to Array of Ip'S
Activerecord User-Supplied Column Name
Bundler Could Not Find Compatible Versions for Gem "Bundler": in Gemfile: