Skip to content
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

Add public and private access modifiers to language #1446

Closed
Ing-Brayan-Martinez opened this issue Feb 9, 2021 · 4 comments
Closed

Add public and private access modifiers to language #1446

Ing-Brayan-Martinez opened this issue Feb 9, 2021 · 4 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@Ing-Brayan-Martinez
Copy link

Good morning, I open this issue to raise the idea in the correct place, all the details are in the following link: Add public and private access modifiers to language

@Ing-Brayan-Martinez Ing-Brayan-Martinez added the feature Proposed language feature that solves one or more problems label Feb 9, 2021
@lrhn
Copy link
Member

lrhn commented Feb 10, 2021

It's still not entirely clear what those modifiers should do. The linked thread has multiple designs, and all of them has some details left unspecified.
It's also not clear what problem this feature is trying to solve. "Not being able to write public and private" is not the problem.

Not being able to declare something as public or private could be a problem, but you can do that (everything is either public or "library private"). So, it would be a lack of some notion of privacy different from library privacy, typically "class privacy". The need for such a thing has not been established (you can declare one class per library, then library privacy is class privacy).

Not liking the syntax of _foo and wanting something different is a problem, but one that I can almost certainly promise we are not going to spend resources solving.

Writing public has no effect when everything is public by default, so let's focus on private.
If we allow you to declare a private int foo() {} method on a class Foo, what would it mean?
One possible approach:


If foo is "class private", it cannot be accessed from outside the class.
What does "inside/outside the class" mean? Code is inside the class if it's part of a member declaration of the class.
What about subclasses? Probably doesn't count (that would be protected), so only inside the class itself.
Meaning that the method is not virtual, it cannot be overridden in a subclass.
What happens if a subclass declares a different member named foo?

  • Either it's allowed, the private member is invisible and the subclass acts as if it's not there.
  • Or it's prohibited, the private member is a normal member, it's just prevented from being accessed or overridden from outside the class. (This is an issue because being "private" is at least as much about preventing name clashes as it is about prohibiting access. The latter can be handled by social constructs, the former cannot).

So if code inside Foo does o.foo() on a receiver of type Foo, it calls Foo.foo. Code outside of Foo cannot call it.
If code inside Foo does o.foo() on a receiver of type which is a subtype of Foo, then if it doesn't declare a different foo, it calls Foo.foo. If the subclass does declare a different (not private) foo, that is called instead, and you have to do (o as Foo).foo() to call the original. Code outside the class can only call the subclass foo.
What about dynamic invocations? If o has type dynamic and you do o.foo() inside Foo and the o object happens to implement Foo, will it call the private method? It's possible, but it adds yet another extra cost to dynamic invocations. At least it's visible at the invocation point that the current class declares a private member with the same name, and you only need the extra overhead if it's possible to hit a private member.

So, in short, it's either

  • a normal interface method which can only be called from inside the class introducing it, and cannot be overridden by something with the same name in a subclass, or
  • It's a special non-virtual method (so subclasses can freely declare something with the same name), which can only be called inside the class which declares it.

This is a possible, consistent definition of a private member, but it's very unclear to me which problem it solves, other than not being able to avoid a _ in the name.

@MatrixDev
Copy link

MatrixDev commented Apr 7, 2021

I'll add few more words, maybe it will be a compromise that everyone needs.

Changes

  • treat all _ as library access modifier (read below)
  • add following keywords:
    private - visible only in class (probably should be forbidden as class modifier as it makes no sense as mentioned by @lrhn )
    library - same as _
    package - same as package in Java, this one is most painful for me personally, those 2k+ files are driving me crazy...
    protected - everyone extending our class (same as in other languages)
    public - public as it is currently, can be omitted for backwards compatibility
    Remark: each next access modifier is a superset of previous one (for ex. package just increases library visibility, etc.)
  • add runtime check to dynamics.
    I can't speak for everyone but even in flutter sources I didn't see that many calls to dynamic directly without cast, so even with added overhead performance impact should not be that huge.
    Also IMHO dynamic itself is very bad and can lead to many bugs. It is much safer to just cast something. And yes, I know there are some limited uses when casting doesn't help, but those are exceptionally rare.

Why so many modifiers?

  • private
    Event when you put multiple classes in a single file, you still need to know what has _ modifier to limit to library usage and what should not be used by other classes even within the same library. Otherwise everything inside library is just public, which is not good.
  • package
    Yes, making classes private (_) and placing everything in one file is in theory the same as package modifier. But on a large projects those files become very big and they are not easy to navigate. Regions somewhat help but not but much.
    I frankly wonder how you guys navigate Flutter sources while developing, some files are 5k+ lines of code. Especially when you need to frequently jump between few classes in the same file.
  • protected
    I can ask the same - why does @protected exist?

Benefits

  • non-breaking change. all code written before will still work
  • _ lovers can continue to use it and have concise syntax
  • no more dumb @protected annotation.
    I think there is no counterargument that plain existence of this annotation tells that something somewhere has gone wrong.
    It must die the same way as @required did, I'll never miss you, thanks Dart team, you've really deserved this one from me ❤️.

Drawbacks

  • two ways to write access modifiers
    IMHO not that big of a problem, _ can be considered as shorthand for private.
  • dynamic performance penalty
    Really, but really, was it even used that much? I'm yet to see any project with regular usage of dynamic that can visibly impact performance.
  • _ conflict with other modifiers
    Let just named modifier override _ or throw compile error, doesn't really matter. Error is probably better.

Bottom Line

Non-breaking compromise that doesn't offend anyone.

@Reprevise
Copy link

One of the benefits of having a private keyword (or priv if you want to shorten it, not sure, could be dumb) is that it's so much easier to declare private named constructor parameters. Personally, I would make _ and private equivalent to each other, private just being another way to do it.

class Foo {
  const Foo({required this.bar});

  final private int bar;
}

@Ing-Brayan-Martinez
Copy link
Author

I think that with the current version of Dart which is 3.1 at the time of creating this post, it already covers much of what was requested regarding class access modifiers, this topic should be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

4 participants