[ruby-core:95881] [Ruby master Feature#16276] For consideration: "private do...end" / "protected do...end"
From:
merch-redmine@...
Date:
2019-11-19 04:27:40 UTC
List:
ruby-core #95881
Issue #16276 has been updated by jeremyevans0 (Jeremy Evans).
Dan0042 (Daniel DeLorme) wrote:
> > - Possibility #3: renders SyntaxError. This requires a massive rewrite of our parser. Theoretically possible but not in practice.
>
> I'm a bit curious about this. My understanding is that a Proc object is not created for every block. So it should be possible to know that `private{ }` is called with a block while `private(&block)` is called with a Proc (and raise an error in the latter case).
It may be possible to know at runtime whether a literal block is passed or not (if not, that is probably an easier change to make). However, you can't really know at parse time (SyntaxError is raised at parse time). Example:
```ruby
class A
class << self
alias priv private
end
priv do
def
end
end
```
> In `invoke_block_from_c_bh` (from vm.c) I can see `switch (vm_block_handler_type(block_handler))` which seems to do exactly that: telling apart the types of block. So in the case of `private`, if `vm_block_handler_type` returns `block_handler_type_proc`, it would make sense to me to raise an error. Or quite possibly I'm misunderstanding something about how this all works.
Since you seem to be in doubt, you should attach a debugger and call with a literal block and call with a block passed via `&`, and see what the difference is.
adh1003 (Andrew Hodgkinson) wrote:
> I'm just asking for a very Ruby-like, clear, simple syntax extension that makes it obvious when a bunch of things are collected inside a specific visibility scope, in passing cleaning up nasty messes like "private_class_method" and solving a couple of (minor) formatting wars in passing.
What you consider making obvious, others may consider clouding the difference between instance methods of the class and singleton methods on the class.
`private_class_method` is just a shortcut. Calling it a nasty mess implies `define_singleton_method` is also a nasty mess. If you always define methods as regular methods, you don't need `private_class_method`:
```ruby
class A
class << self
def public_singleton_method
end
private
def private_singleton_method
end
end
def public_instance_method
end
private
def private_instance_method
end
end
```
----------------------------------------
Feature #16276: For consideration: "private do...end" / "protected do...end"
https://bugs.ruby-lang.org/issues/16276#change-82718
* 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阮e - 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>