Private setters can be called by self, why not getters?

class Counter
  def initialize
    self.count = 0
  end

  def increment1
    self.count = count + 1
  end

  def increment2
    self.count = self.count + 1
  end

  def increment3
    self.count += 1
  end

  private

  attr_accessor :count
end

counter = Counter.new
counter.increment1 rescue $!.message # => 1
counter.increment2 rescue $!.message # => "private method `count' called
for #<Counter:0x007f8c998c7c60 @count=1>"
counter.increment3 rescue $!.message # => "private method `count' called
for #<Counter:0x007f8c998c7c60 @count=1>"
(counter.count = 0) rescue $!.message # => "private method `count=' called
for #<Counter:0x007f8c998c7c60 @count=1>"

So in Counter#increment1, we can see that we can invoke Counter#count=,
even though it's private. But in increment2, we try `self.count`, which
raises a NoMethodError, because we're calling a private method with an
explicit receiver. But isn't that what we're doing when we say `self.count
= ...` ? Somehow it works for self, but we can see in the last example that
we can't do this from outside.

So why can we invoke private setters with self as the explicit receiver,
but not private getters? This comes up primarily because what we really
want to do is increment3, but this whole time I've thought it was the
setter that was causing the issue, realized last night it was the getter
(b/c I was going to recommend making these protected instead of private, in
order to avoid this issue).

-Josh

We not only can - we must. In your example, `count = 0` would set the local variable `count`, not call the `count=` method.

Whether this is good language design is anyone's guess. This conflict itself is a pretty natural consequence of implicit `self.`, though.

Personally I tend to avoid public/protected/private altogether and use the convention of prefixing private methods with `_`.

···

On Sun, 31 Mar 2013 15:51:31 +0200, Josh Cheek <josh.cheek@gmail.com> wrote:

So why can we invoke private setters with self as the explicit receiver,

--
Matma Rex

You have to define setters and getters in ruby more carefully, when you
intend to use them as private methods! You have to separate what is
getter and what is setter, especially, when one and the same symbol can
be used for both operations simultaneously as is the case in combined
assignment operators such as +=, -=, *=, /= ...

Here is how you manage the getters and setters when setter but not
getter should be private:

···

---------------------------------------------------
  #!/usr/bin/env ruby
  class Counter
    attr_reader :count
    attr_writer :count
    private :count=

    def initialize; self.count = 0; end
    def show_count; puts "Count=#{count}"; end
    def increment1; self.count = count + 1; end
    def increment2; self.count = self.count + 1; end
    def increment3; self.count += 1; rescue; end
  end

  o = Counter.new
  o.show_count
  o.increment1
  o.show_count #=> Count=0
  o.increment2
  o.show_count #=> Count=1
  o.increment3
  o.show_count #=> Count=2

  puts "Explicit get: Count=#{o.count}" #=> Explicit get: Count=2
  o.count=1234
  ----------------------------------------------------

Indeed, if you do not care or are ignorant of oo, you can resort to
devices like the underscore as suggested above.

--
Posted via http://www.ruby-forum.com/.

Josh Cheek wrote in post #1103861:

(b/c I was going to recommend making these protected instead of private,
in order to avoid this issue).

-Josh

You are right, making these protected instead of private seems to be the
best solution, unless you need to shield access to your getters and
setters from subclasses too. Taking into account, that instance
variables in Ruby are by design public, all this accessor encapsulation
is rather 'non-ruby-ish', nevertheless, your suggestion deserves to be
noticed!

The following code works exactly as expected, and you can even use only
the simplest form of accessor declaration {{ attr_accessor :count }} in
the private section, without bothering to separate the getter from the
setter method.

···

--------------------------------------------------------------------
  #!/usr/bin/env ruby
  class Counter
    def initialize; self.count = 0; end
    def show_count; puts "Count=#{count}"; end
    def increment1; self.count = count + 1; end
    def increment2; self.count = self.count + 1; end
    def increment3; self.count += 1; end
   protected
    attr_accessor :count
  end

  o = Counter.new
  o.show_count #=> Count=0
  o.increment1
  o.show_count #=> Count=1
  o.increment2
  o.show_count #=> Count=2
  o.increment3
  o.show_count #=> Count=3

  puts "Explicit get: Count=#{o.count}" #=> fails with exception
  o.count=1234 #=> would properly fail too
  --------------------------------------------------------------------

Very good, Josh!
Regards, igor

--
Posted via http://www.ruby-forum.com/\.

Josh Cheek wrote in post #1103951:

You can't hide methods from subclasses.

I, know you cannot hide methods from subclasses in Ruby. On my quick
initial reading of your first post in this thread, I had an impression
you wanted to do so, as it looked, by imposing your own, BTW, rather
dubious convention, to use {{self}} on all instance methods defined in a
class in which they were defined. Insisting to use {{self}}, would
indeed, prevent a subclass to access a private method of a superclass,
while calling protected or public methods in superclasses this way,
would not!

···

On Mon, Apr 1, 2013 at 1:16 AM, Igor Pirnovar <lists@ruby-forum.com> > wrote:

  ----------------------------------------------------------------------
  class A;def who; base;end; private;def base; puts "Class A";end; end
  class B<A;def who; self.base;end; def base;puts "B:overriden";end; end
  class C<A;def who; self.base;end; end
  a = A.new
  a.who #=> Class A
  b = B.new
  b.who #=> B:overriden
  c = C.new
  c.who #=> Exception NoMethodError
  ----------------------------------------------------------------------

Hence, there is a way, to shield access to inherited methods, providing
you obey your self-imposed convention.

It's not clear to me what you mean about variables being public.

You have access to any instance variable in any class in the inheritance
hierarchy, as long as you do not shield a variable by defining it with
the same name as one in a superclass:

  ---------------------------------------------------------------------
  class A; def initialize; @av="A";end; private; attr_accessor :av; end
  class B<A; def private_instvar_of_super; puts "@av=#@av"; end; end
  b = B.new
  b.private_instvar_of_super #=> @av=A
  ---------------------------------------------------------------------

But, you seem to know this, as you obviously know for the benefits of
using accessors, only for some reason, you would not see this
unrestricted instance variable visibility and access as public?

I rarely worry about my Ruby being non-ruby-ish. ....

I never said that Ruby is non-ruby-ish! Non-ruby-ish is perhaps
someone's programming style, and certainly, trying to make instance
variables (or as, I am sure, you'd correct me, access to them) private
in Ruby, qualifies for such a characterization.

Ruby allows you to be as free or as strict as you wish! It does not help
you too much to enforce your whims, or your own programming conventions,
though. There is absolutely nothing preventing you to accomplish the
required flexibility "to implement your public methods by using private
data" or "frequently change variable names, move things around, change
implementations, etc." In fact I applaud you to find a way to turn all
accessor methods to behave almost as if they were private, by using
protected instead.

Also, converting classes and structures back and forth is highly
unreliable practice in Ruby, particularly because of the inconsistencies
when using accessor methods, which may fail to work in structures. If
you wish to enjoy Ruby oo paradigm, you better think twice if you really
want to use structs. But, I guess, you'd have to be the "Rubyist", to
know this?

I do not think, your presenting yourself as a cavalier, who believes he
is entrapped in some fictitious Ruby constraints, is an accurate
description. I wish one day you'd realize, Ruby is cavalier to you
instead.

All the best, igor

--
Posted via http://www.ruby-forum.com/\.

I rarely want to expose either. In my code example, why can I call
`self.count=` but not `self.count` ?

···

On Sun, Mar 31, 2013 at 1:19 PM, Igor Pirnovar <lists@ruby-forum.com> wrote:

You have to define setters and getters in ruby more carefully, when you
intend to use them as private methods! You have to separate what is
getter and what is setter, especially, when one and the same symbol can
be used for both operations simultaneously as is the case in combined
assignment operators such as +=, -=, *=, /= ...

Here is how you manage the getters and setters when setter but not
getter should be private:

  ---------------------------------------------------
  #!/usr/bin/env ruby
  class Counter
    attr_reader :count
    attr_writer :count
    private :count=

    def initialize; self.count = 0; end
    def show_count; puts "Count=#{count}"; end
    def increment1; self.count = count + 1; end
    def increment2; self.count = self.count + 1; end
    def increment3; self.count += 1; rescue; end
  end

  o = Counter.new
  o.show_count
  o.increment1
  o.show_count #=> Count=0
  o.increment2
  o.show_count #=> Count=1
  o.increment3
  o.show_count #=> Count=2

  puts "Explicit get: Count=#{o.count}" #=> Explicit get: Count=2
  o.count=1234
  ----------------------------------------------------

Indeed, if you do not care or are ignorant of oo, you can resort to
devices like the underscore as suggested above.

Josh Cheek wrote in post #1103861:
> (b/c I was going to recommend making these protected instead of private,
> in order to avoid this issue).
>
> -Josh

You are right, making these protected instead of private seems to be the
best solution, unless you need to shield access to your getters and
setters from subclasses too.

You can't hide methods from subclasses.

Super = Class.new { private; attr_accessor :a }
Sub = Class.new Super
Sub.private_instance_methods.grep /^a=?$/ # => [:a=, :a]
Sub.instance_method(:a).owner # => Super

Taking into account, that instance
variables in Ruby are by design public, all this accessor encapsulation
is rather 'non-ruby-ish', nevertheless, your suggestion deserves to be
noticed!

It's not clear to me what you mean about variables being public.

I rarely worry about my Ruby being non-ruby-ish. In this particular case, I
want to do this because I internally want to implement my public methods by
using private data. I know I could use instance variables, but I frequently
change variable names, move things around, change implementations, etc. If
I use an ivar, I start handing nil around, or setting the wrong variable.
If I use a method, I immediately get a NoMethodError. As the Pragmatic
Programmers say, "Crash Early". Furthermore, if I use a method, I can
encapsulate access from even my own object, which would allow for swapping
implementations out (e.g. switching from attr_accessor to a Struct, which
would require updating all access points if they used ivars). I know most
Rubyists aren't as bothered by things like this, and maybe they're right,
but when I disregard these concerns, I don't feel free, I feel cavalier.

-Josh

···

On Mon, Apr 1, 2013 at 1:16 AM, Igor Pirnovar <lists@ruby-forum.com> wrote:

It's a hack, because setters require you to use "self.count=" syntax,
whereas you can just call a getter with "count"

Since there's no way to call a private setter except "self.count=", Ruby
permits "self" as a receiver for this even if it's a private method.

Clearly the more consistent thing to do would be for private methods to
always permit "self" as a receiver. It's a bit odd Ruby doesn't allow this,
IMO

···

On Sun, Mar 31, 2013 at 12:20 PM, Josh Cheek <josh.cheek@gmail.com> wrote:

I rarely want to expose either. In my code example, why can I call
`self.count=` but not `self.count` ?

--
Tony Arcieri

Josh Cheek wrote in post #1103882:

I rarely want to expose either. In my code example, why can I call
`self.count=` but not `self.count` ?

As Tony asid, it's a hack! Private methods (except attribute_writers) in
Ruby can not be called on explicit receivers! That's how private is
defined in Ruby, namely, no calls on explicuit receivers. Likwise the
combined assignment operators will not work, if you define accesors as
private. Sorry, I failed to show that clearly in my example segregating
the private setter and public getter above :frowning:

Cheers, igor

···

--
Posted via http://www.ruby-forum.com/\.