-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Visibility modifies for types #2950
Comments
So much 👍 for the idea in general! Personally, my favorite usage is what you described in "protected constant" where the constant may be accessed "bare" ( |
I think types should always be accessible by a fully qualified name. For private types, the restrictions should be that only inside the container type the private one is accessible (without :: or with the full name). I think there might be chances of ambiguity that could be resolved with fully qualified name and we might get stuck if we don't allow them. |
Between private/protected contained type, maybe private should be visible only in the same file. |
Types as project internal implementation details is great! I regularly create types with :nodoc: and it would make things cleaner and better for public APIs. But what if the types above grow a little big, or we add more types that need Bar? we'd like to extract them to their own file, but we can't if we don't want to lose the visibility, or we'd have to reintroduce the :nodoc: trick... |
Keep in mind we need simple rules that cover a wide variety of cases in a consistent and obvious manner, I tried to start a list of possible cases, yet that doesn't even include reopening private or protected types (within or in a new file), nor adding private or protected types to a reopened type from a new file, which adds about another 60 or so cases easily. private class A; end
protected class B; end
module Mod
private class C; end
protected class D; end
end
class Parent
include Mod
private class E; end
protected class F; end
end
module Mod2
private class G; end
protected class H; end
end
module Mod3
include Mod2
end
A.new # Case 1
B.new # Case 2
Mod::C.new # Case 3
Mod::D.new # Case 4
Parent::C.new # Case 5
Parent::D.new # Case 6
Parent::E.new # Case 7
Parent::F.new # Case 8
Mod3::G.new # Case 9
Mod3::H.new # Case 10
class Child < Parent
include Mod2
def initialize
A.new # Case 11
B.new # Case 12
C.new # Case 13
D.new # Case 14
Mod::C.new # Case 15
Mod::D.new # Case 16
E.new # Case 17
F.new # Case 18
Parent::E.new # Case 19
Parent::F.new # Case 20
G.new # Case 21
H.new # Case 22
Mod2::G.new # Case 23
Mod2::H.new # Case 24
Mod3::G.new # Case 25
Mod3::H.new # Case 26
end
end
class Child2 < Parent
include Mod3
def initialize
G.new # Case 27
H.new # Case 28
end
end
# New file
require "first_file"
A.new # Case 29
B.new # Case 30
Mod::C.new # Case 31
Mod::D.new # Case 32
Parent::C.new # Case 33
Parent::D.new # Case 34
Parent::E.new # Case 35
Parent::F.new # Case 36
Mod3::G.new # Case 37
Mod3::H.new # Case 38
class Child3 < Parent
include Mod2
def initialize
A.new # Case 39
B.new # Case 40
C.new # Case 41
D.new # Case 42
Mod::C.new # Case 43
Mod::D.new # Case 44
E.new # Case 45
F.new # Case 46
Parent::E.new # Case 47
Parent::F.new # Case 48
G.new # Case 49
H.new # Case 50
Mod2::G.new # Case 51
Mod2::H.new # Case 52
Mod3::G.new # Case 53
Mod3::H.new # Case 54
end
end
class Child4 < Parent
include Mod3
def initialize
G.new # Case 55
H.new # Case 56
end
end
# New file
require "first_file"
include Mod
C.new # Case 57
D.new # Case 58
Mod::C.new # Case 59
Mod::D.new # Case 60
# New file
require "first_file"
include Mod3
G.new # Case 61
H.new # Case 62
Mod3::G.new # Case 63
Mod3::H.new # Case 64 |
What if the types are private on the module level, not the file level? e.g. module X
private class Y
end
end
X::Y # error
module X
class Z < Y # OK
end
end It would be more in line with how class private variables work. |
I don't think that many cases need to be considered. Only the target type and the location where the type is requested need to be analyzed. For protected it means "inside the same hierarchy or namespace", similar to protected methods. It's for private (non-top-level) types I'm not sure about the semantics. I think the best thing to do here is for me to prepare a PR with specs and we can discuss on real cases, and try new cases and see how they behave, and if they should behave like that. |
Are private types in class also a file-private types? (I wish no, that we can continue modifying these classes in other files like what we do in Ruby or Crystal) I think private/protected types are useful that C# and Java also have this feature. |
They would work similar to methods: a private top-level type is private to the file. A private/protected type inside another type is visible inside that namespace, but not outside it. I'm still having issues thinking what would the difference between private and protected be, and whether we need two visibility modifiers. |
In Java, protected inner class can only be visible in the same package, and private is only for the current class (outer class). (ref) So maybe we can restrict protected types can only be seen in the same namespace (class, module) and the sub-namespaces, and private only be seen in the same namespace. For example, the table below will show what can we see under other classes if A::B has different modifier:
|
And what if I do Like this: class Foo
private class Bar
end
def self.foo
Bar.new
end
end
Foo.foo #=> instance of Foo::Bar or cannot access?
Foo.foo.class #=> what will happend?
Foo.foo.class.new #=> what will happend? |
Now it is possible to do what I've always wanted: private macros that work not just for one file. module My
private module Macros
macro mac(a)
7 + {{a}}
end
end
end module My
def self.test
Macros.mac(5)
end
end
p My.test But it is possible only indirectly, macros still remain a black sheep, because |
@BlaXpirit You mean something like this? (doesn't work, just to understand): class Foo
private macro foo
end
foo # OK
end
Foo.foo # Error |
@asterite, I thought my example would explain it. Something like this: module My
private macro mac(a)
7 + {{a}}
end
end require "./the_file_above"
module My
def self.test
mac(5) # OK
end
end
p My.test
My.mac(1) # Error Though your example is not necessarily different from what I want (in fact, it would make a lot of sense and be consistent). I just have a specific use case with modules. |
@BlaXpirit I implemented this in #3747. |
Right now
private
andprotected
can't be applied to types, only to methods, but it would be nice if we could do so.file-private types
Similar to file-private methods and macros:
private types
A private type can only be found from within the type where it's contained, and without "prefixes" (
::
):protected types
A protected type can be found from within the type where it's contained, and allows "prefixes" (
::
):This is an RFC so we can discuss the behavior.
I'm not sure about the difference between private and protected types, but I feel the same about private and protected methods (I think "protected" is enough for all cases). Maybe we can remove "protected" from the language and make "private" have the current meaning of "protected".
The text was updated successfully, but these errors were encountered: