dart-lang / language

Design of the Dart language
Other
2.67k stars 205 forks source link

About the Dart lang assignment of class private member variables and method #855

Open toop opened 4 years ago

toop commented 4 years ago

Dart lang is great!I very like the Dart lang! But the Dart currently only has library-private declarations,Strongly recommended that assignment of private member variables should be restrict to the current class by default!!!Dart should has class-private declarations,a name prefixed with an underscore (e.g. age) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail。Or, in order to be compatible with the old version, it is strongly recommended to add double underscores (e.g. _age) as class-private declarations,In no case should an instance call or modify a private variable or call a private method,Private is absolutely private and can't be accessed anywhere except itself!!! Most of the time, we need to hide some sensitive information and methods, make them private, only allow to assign and call in the class, and only allow public variables and methods in the instance; just like Python " ". This is very important!!! I know that big companies like Google don't expect to improve on this proposal. If so, more's the pity!

Example code,file name test.dart:



main() {
  var student = Student(1);
  student.name = "Peter";

  print("private _age is ${student._age}");  //Must No access,this`s private member variables
  student._age =500;     //Must No assignment

  student.age =600;
  print("now age is ${student.age}");  //now age is 0

  student.say();
  //pub method call private method
  //Private method say hello!

  student._say();   //Must No call,this`s private member method
}

class Student {
  int id = -1;
  String name;
  int _age = -1;

  Student(this.id, {this.name});

  int get age => _age;
  int set age(v) {
    if (v > 18 && v < 180){
       _age = v; 
    }else {
       _age = 0;
    }

  }

  //Private method
  void _say() {          
    print("Private method say hello!");
  }

  void say(){
    print("pub method call private method");
    this._say();
  }
}

```kotlin code
class Greeter() {
    private var a =1
    fun greet() {
        println("Hello")
    }

    private fun test(){
        println("test")
    }
}
​
fun main() {
    var d = Greeter()
    d.greet()
    d.a = 12
    d.test()

}
//Cannot access 'a': it is private in 'Greeter'
//Cannot access 'test': it is private in 'Greeter'
lrhn commented 4 years ago

Dart's only notion of privacy is library privacy. Within that context, private most certainly means private. Nobody outside the library itself can even speak about the library private names.

You are saying that the unit of privacy should be the class, not the library.

That's a choice. It's not the choice that Dart has picked for a number of reasons. One of them is that not everything in Dart is inside a class. If privacy only works for class members, then there is no way to make a private top-level function or type declaration. Another is that the library is the unit of compilation in Dart. Anyone who can write code in the same library can also choose to make your private members public. The library is like a unit of trust. Code in the same library can inherently be trusted because it's assumed to be written by the same people, and any of them can change anything anyway. And Dart is generally a permissive program which trusts its programmers to do the right thing. You have ways to structure your code and get safety and static checking, but if you want to write your entire program on one file and use no type other than dynamic, then you can.

Since you can choose to declare only one class in each library, you can choose to have class based privacy in Dart too. Or you can choose to declare some friend functions and classes in the same library. So far, library privacy has shown itself to be quite flexible. The one kind of access that it doesn't handle well is "protected", but I think it handles privacy well exactly because it allows you to choose how much code you put in the same library.

In this example, putting main in the same library as Student was what allows them to interact through private members. The answer would be "then don't do that".

toop commented 4 years ago

I know that big company like Google don't expect to improve on this proposal, more's the pity!

toop commented 4 years ago

Or, in order to be compatible with the old version, it is strongly recommended to add double underscores (e.g. _ _age) as class-private declarations,In no case should an instance call or modify a private variable or call a private method,Private is absolutely private and can't be accessed anywhere except itself!!!

toop commented 4 years ago

The kotlin language has done this。



class Greeter() {
    private var a =1
    fun greet() {
        println("Hello")
    }

    private fun test(){
        println("test")
    }
}
​
fun main() {
    var d = Greeter()
    d.greet()
    d.a = 12
    d.test()

}
//Cannot access 'a': it is private in 'Greeter'
//Cannot access 'test': it is private in 'Greeter'
toop commented 4 years ago

The "don't do that" not as the best programming specification,The library privacy can't achieve the best class encapsulation , can't achieve the absolute security!!!

lrhn commented 4 years ago

Access control is not about security. The unit of Dart security is the isolate. If the dart:mirrors library is available, then nothing can protect one library from another in the same isolate.

Dart library privacy is a tool for avoiding naming conflicts and for separating a private implementation API from a public interface API, while still allowing you to write both in the same library. It protects your code from accidental misuses, but it is not intended to prevent deliberate malicious use. It only stops other people from incorrectly accessing your implementation API, not yourself, because that's a waste of time. You can just rewrite the code anyway. Here, "yourself" means the author of a library.

Dart has chosen to make the library the unit of API and implementation, not a single class. That makes sense for a language which has top-level declarations. Not everything is inside a class. (Although, again, a library can choose to contain a single class, then library privacy is class privacy for that class).

So, to be more concrete, which problem is it that you want a stronger privacy control to solve?

toop commented 4 years ago

Flexible, convenient, more direct, more rigorous specifications, stronger protection and encapsulation of private members

lrhn commented 4 years ago

Those are all great concepts in the abstract.

Exactly how will stronger privacy controls improve flexibility and convenience? (If anything, it sounds like adding more restrictions will reduce flexibility and convenience, which is why people usually want more restrictions - it shouldn't be too convenient!)

Why is encapsulation of private members to a single class, rather than a single library, an advantage? Class privacy protects use of the class' private members against people who can rewrite the class at will, so it's not really stronger protection, just a minor inconvenience if they really insist. It's useful as documentation that this member is not intended for external use, but what's the harm if the use is in the same code. It's not like one person will change the private members and not notice that the other using them, the errors occur immediately in the same file.

You can write one-class-per-library and get exactly what you ask for. Putting more classes or functions into the same library is a way to specify that those classes are more strongly linked. That gives you flexibility and convenience.

The use-case I can see is wanting to use both library privacy for some things and class privacy for other things, but I still don't see what advantage class privacy actually give you - it's already very directly specified that the member is (library) private, and the author of that library is the one in charge of how everything in the library is used already.

munificent commented 4 years ago

If you'd like your members to be private to each class, you can simply place each class in its own library. I don't see much compelling technical or usability argument that adding class privacy in addition to library privacy is worth the complexity cost. We're already considering package private and protected, and there's only so many levels of access control that humans can handle.

ayporos commented 4 years ago

Since you can choose to declare only one class in each library, you can choose to have class based privacy in Dart too.

Initial data:

  1. LibA with one file:
    
    class Foo {
    final _Bar bar = _Bar();
    }

class _Bar { final String name = "Bar"; }

2. LibB with a method:

void test() { final Foo foo = Foo(); print(foo.bar.name); }



Without running the code can someone guess an outcome of calling `test`?
igotfr commented 4 years ago

Dart confuse file with scope, it's a mistake that should never be made, unfortunately Vue and Svelte also makes this mistake

munificent commented 4 years ago

Other languages have the exact same behavior. For example, here's Java:

public class Main
{
  public static void main(String[] args)
  {
    System.out.print(new Foo().getBar());
  }
}

public class Foo 
{
  private static class Bar {}

  public Bar getBar()
  {
    return new Bar();
  }
}
ayporos commented 4 years ago

Did you intentionally omit access to 'name' field from private class in your example ('print(foo.bar.name);' in dart code)?

toop commented 4 years ago

Or, in order to be compatible with the old version, it is strongly recommended to add double underscores (e.g. _ _age) as class-private declarations,In no case should an instance call or modify a private variable or call a private method,private members and private methods is absolutely private for single class and can't be accessed anywhere except itself!!! scope minimum,encapsulation to a single class!

munificent commented 4 years ago

Did you intentionally omit access to 'name' field from private class in your example ('print(foo.bar.name);' in dart code)?

No, that was unintentional, sorry. And, indeed, when I try to access that, I get an error about inconsistent accessibility. Today I learned something new!

lrhn commented 4 years ago

Java has four privacy levels: Class private, Protected, Package Private (aka "default", or "same directory") and Public. They are all there for a reason. None of them are necessary or inherently natural, they are there to solve specific problems.

Dart has only two: Library ("same file") and public. That's because some of the reasons from Java collapse to one for Dart code.

Since Java can only have one public class in each file, a Java package roughly corresponds to a Dart library as a unit of cooperating class declarations. Java package privacy allows the members of a package to be "friends" with each other and share some implementation. Since Java classes are still in separate files and can be edited independently, it also makes sense to have class private members to avoid accidental name conflicts with code that you cannot currently see.

Dart's library is both the unit of editing and cooperating. As such, it is sufficient to have library privacy as both "cooperation unit privacy" and "editing unit privacy".

(I do miss Dart having "protected" - class hierarchy privacy).

ayporos commented 4 years ago

I'd like to have at least lint warning about making "library" class available to outside world like in https://github.com/dart-lang/language/issues/855#issuecomment-611584597

toop commented 4 years ago

Example Swift Code


class P{
    private var t:String="123"

    func sett(x:String){
        self.t=x
    }

    func printT(){
        print(self.t)
    }

    private func ss(){
        print("test!")
    }
}

var p=P()
p.sett(x:"TEST")
p.printT()  // => out: TEST
p.t="ddd"  // =>error: 't' is inaccessible due to 'private' protection level
print(p.t) // =>error: 't' is inaccessible due to 'private' protection level
p.ss()     // =>error: 'ss' is inaccessible due to 'private' protection level
toop commented 4 years ago

Hope to adopt the suggestions and join the new version of the plan,Thanks!Leaf Petersen (@leafpetersen), Lasse R.H. Nielsen (@lrhn), Bob Nystrom (@munificent), Erik Ernst (@eernstg),Michael Thomsen (@mit-mit)

SzunTibor commented 4 years ago

What's missing here, I think, is the other side of the coin.
When we have class-based privacy, the non-member public functions in the library are part of the interface that is guaranteed to be implementation independent. If we let those functions access private members that guarantee disappears. Of course, we can just write those functions in a separate file / library and get those guarantees back, but it would be convinient indeed to mark that as part of the interface and not have to import 2 files.