ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: shevegen@gmail.com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:95516] [Ruby master Feature#16276] For consideration: "private do...end" / "protected do...end"
Date: Wed, 23 Oct 2019 21:17:57 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-82290.20191023211756.806f730bceb25d4b@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-16276.20191023194923@ruby-lang.org

Issue #16276 has been updated by shevegen (Robert A. Heiler).


In general I agree with the proposal, or at the least with the basic gist of it, e. g.:


private {
  do_stuff
}

public {
  do_stuff
}

I do not know how old private/public distinction is in ruby but I think matz added
this very early on, perhaps even in the first public releases. I remember in the
old pickaxe, it was explained that "private" and "public" are sort of typically
used as "toggling the state" that is - you write code, and then you may add
e. g. "private", and write all the code that is, well, private.

Personally I do not use the private and public distinction. This is mostly in the
way how you (as a ruby user) may want to use ruby. Some prefer a more java-centric
OOP model; perhaps this was a use case as to why .public_send() was added by
matz at a later time (I think .send() was much older than .public_send() but
I do not know when the latter was added). In my opinion, using .send and avoiding
private, is more "idiomatic", but this depends on the point of view. Personally
I like the self/smalltalk view on OOP more than the C++/java view. Ruby has its
own view, sort of; while I think the primary focus is on OOP, ruby has always
been multi-paradigm. I think matz likes to play with ideas and concepts. :)

But anyway, back to the suggestion. The reason why I am +1 for this proposal,
even though I personally do not use that distinction really, is because I 
actually would find it convenient. I don't know if this may create incompatibilites
or not, but purely from a convenience point of view, I think that would be a
good idea.

There are a few parts I disagree with your proposal though. For example,
you wrote:

> to have that protection level

I am not putting too emphasis here really, so excuse my nit-picking, but IMO ruby 
does not have a strong "protection" level because I think it would not be completely
well aligned with ruby's philosophy of flexibility, more-than-one-way and in general
letting people decide what they want to use it, and how. (Compare this to python in
many ways, which pursues a different model.) For example, we can use .send() to 
"get access" to literally anything; we can obtain instance variable and change them
if we want to. I love this dynamic nature. Others may dislike it, if they think
that ruby should be less dynamic. When you have "two sides", one saying that a
particular use case is bad, the other saying that it is good, it is difficult
to align them with the same thought, since these thoughts are orthogonal and
conflicting. I'd always reason in favour of .send() for example and never use
.public_send() myself. :)

But I am a bit nit-picking here, so don't mind this comment too much.

> It is not idiomatic in Ruby to indent method definitions after such declarations

I would not use the word "idiomatic", but I actually agree with you for another
reason. Indenting code can be a bit annoying. Typically most people may tend
to use two spaces per indent level. I actually ignore that when I define classes
in modules, e. g.:

    module Foo
    module Bar
      class Cat

^^^ module Bar "should" be two spaces to the right, but I much prefer it to the
very left, and then using just one indent for class. Now - I don't expect many
other people to use this, but I liked it; and while this is not completely related
to the same use case and the description of the feature here, I concur with you 
here. I actually never indent when private/public is used. Actually, I do sometimes
use private, but I then use something crazy:

    def foo
    end; private :foo

This is more work but ... I like that I don't have to indent to the right. :P

So for this and similar reason, I actually agree with your premise mostly, 
even though I do not use the same argument(s).

> so it becomes at a glance very hard to see what a method's protection level
> is when just diving into a piece of source code.

Yup - I sort of agree with you here. Even though I guess we both may write
ruby code differently. :)

Makes sense to me what you write in this context.

> As noted in the pseudocode above, we can clean up some of the issues around
> the special syntax needed for "private constants", too.

I have no problem either way but I think "constants" is a bit of a misnomer
in general. The ruby philosophy here is more that ruby allows you to change
it if you want to. Which I think makes sense, oddly enough; at the same 
time, I remember apeiros wondered about the name constant, and I sort of agree
because people may assume that "a constant may never change". Which is not 
wrong, either. Just more-than-one-way to look at something (perhaps it should
have another name than constant, so people don't get confused).

This is also a bit strange when it comes to "private" constants in ruby. Can
these be changed? Should these be changed? Are there even really "private"
constants in ruby?

I actually really don't know. It has been rare that I used constants "dynamically"
and changed them. These days I tend to use @foo toplevel instance variable more,
like:

    module Foo
      @bar = {}

and work from here IF it has to be dynamic (aka data that is to be changed for
some reason or another).

> Does the use of a block-like syntax imply we should support things like Procs too? 

Aren't blocks in ruby in general procs too? But anyway, to answer the question - I
don't think every method supports blocks uniformly strongly, meaning that some
methods make use of blocks more, and other methods don't. To me blocks are more
like an additional argument that is more flexible (usually). Sort of you can use
it if you want to - but you don't have to. So from here, I don't really see a 
problem if private/public were to have a {} block variant.

There may perhaps be other issues, though, such as backwards incompatibility. I
guess this all has to be discussed and of course you have to ask matz about the
design consideration for private/public. Perhaps there was a reason why the
block variant was not considered or used or there were some other problems.


----------------------------------------
Feature #16276: For consideration: "private do...end" / "protected do...end"
https://bugs.ruby-lang.org/issues/16276#change-82290

* Author: adh1003 (Andrew Hodgkinson)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
Private or protected declarations in Ruby classes are problematic. The single, standalone `public`, `private` or `protected` statements cause all following methods - *except* "private" class methods, notably - to have that protection level. It is not idiomatic in Ruby to indent method definitions after such declarations, so it becomes at a glance very hard to see what a method's protection level is when just diving into a piece of source code. One must carefully scroll *up* the code searching for a relevant declaration (easily missed, when everything's at the same indentation level) or have an IDE sufficiently advanced to give you that information automatically (and none of the lightweight editors I prefer personally have yet to support this). Forcibly indenting code after declarations helps, but most Ruby developers find this unfamiliar and most auto-formatters/linters will reset it or, at best, complain. Further, the difficulty in defining private *class* methods or constants tells us that perhaps there's more we should do here - but of course, we want to maintain backwards compatibility.

On the face of it, I can't see much in the way of allowing the `public`, `private` or `protected` declarations to - *optionally* - support a block-like syntax.

```
class Foo

  # ...there may be prior old-school public/private/protected declarations...

  def method_at_whatever_traditional_ruby_protection_level_applies
    puts "I'm traditional"
  end

  private do
    def some_private_instance_method
      puts "I'm private"
    end

    def self.some_private_class_method
      puts "I'm also private - principle of least surprise"
    end

    NO_NEED_FOR_PRIVATE_CONSTANT_DECLARATIONS_EITHER = "private"
  end

  def another_method_at_whatever_traditional_ruby_protection_level_applies
    puts "I'm also traditional"
  end

end
```

My suggestion here confines all `public do...end`, `protected do...end` or `private do...end` protections strictly to the confines of the block alone. Outside the block - both before and after - traditional Ruby protection semantics apply, allowing one to add new block-based protection-enclosed method declarations inside any existing code base without fear of accidentally changing the protection level of any methods defined below the new block. As noted in the pseudocode above, we can clean up some of the issues around the special syntax needed for "private constants", too.

I see a lot of wins in here but I'm aware I may be naïve - for example, arising unanswered questions include:

* Is the use of a block-like syntax making unwarranted assumptions about what the Ruby compiler can do during its various parsing phases?
* Does the use of a block-like syntax imply we should support things like Procs too? (I *think* probably not - I see this as just syntax sugar to provide a new feature reusing a familiar idiom but without diving down any other rabbit holes, at least not in the first implementation)

I've no idea how one would go about implementing this inside Ruby Core, as I've never tackled that before. If someone is keen to pick up the feature, great! Alternatively, if a rough idea of how it *might* be implemented could be sketched out, then I might be able to have a go at implementation myself and submit a PR - assuming anyone is keen on the idea in the first place `:-)`




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

  parent reply	other threads:[~2019-10-23 21:18 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <redmine.issue-16276.20191023194923@ruby-lang.org>
2019-10-23 19:49 ` [ruby-core:95514] [Ruby master Feature#16276] For consideration: "private do...end" / "protected do...end" ahodgkin
2019-10-23 21:17 ` shevegen [this message]
2019-10-23 21:44 ` [ruby-core:95517] " shevegen
2019-10-24  1:58 ` [ruby-core:95523] " duerst
2019-10-24  2:19 ` [ruby-core:95524] " shyouhei
2019-10-24  6:29 ` [ruby-core:95527] " ruby-core
2019-10-29 20:38 ` [ruby-core:95582] " ahodgkin
2019-10-29 21:17 ` [ruby-core:95583] " eregontp
2019-10-29 21:51 ` [ruby-core:95584] " merch-redmine
2019-10-30  1:42 ` [ruby-core:95585] " shyouhei
2019-10-30 19:08 ` [ruby-core:95591] " ahodgkin
2019-10-30 20:04 ` [ruby-core:95593] " merch-redmine
2019-11-01  3:21   ` [ruby-core:95626] " Austin Ziegler
2019-10-30 21:14 ` [ruby-core:95595] " XrXr
2019-10-31  5:03 ` [ruby-core:95600] " mame
2019-10-31  5:27 ` [ruby-core:95601] " duerst
2019-10-31 13:35 ` [ruby-core:95607] " daniel
2019-10-31 15:10 ` [ruby-core:95609] " merch-redmine
2019-10-31 17:43 ` [ruby-core:95612] " daniel
2019-10-31 18:08 ` [ruby-core:95613] " merch-redmine
2019-10-31 19:11 ` [ruby-core:95617] " daniel
2019-10-31 19:30 ` [ruby-core:95618] " XrXr
2019-11-01  1:54 ` [ruby-core:95622] " daniel
2019-11-01  2:42 ` [ruby-core:95624] " shyouhei
2019-11-07  0:02 ` [ruby-core:95732] " ahodgkin
2019-11-07  5:09 ` [ruby-core:95740] " mame
2019-11-07 14:43 ` [ruby-core:95743] " daniel
2019-11-19  0:59 ` [ruby-core:95880] " ahodgkin
2019-11-19  4:40   ` [ruby-core:95882] " Martin J. Dürst
2019-11-19  4:27 ` [ruby-core:95881] " merch-redmine

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-82290.20191023211756.806f730bceb25d4b@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).