Forbid access to private members even if they are from classes defined in the same library

69 views
Skip to first unread message

Cristian Garcia

unread,
May 6, 2015, 4:20:34 PM5/6/15
to mi...@dartlang.org
I am faced with this 3rd party code

Future run(Connection c, [global_optargs]){
 
if(c == null)
 
throw new RqlDriverError("RqlQuery.run must be given a connection to run on.");
 
if(global_optargs == null)
 global_optargs
= {};
 
return c._start(this, global_optargs);
}

where `Connection` is a custom classes defined in that 3rd party library and I wanted to implement that interface (I was basically just creating a proxy object). Now notice the last line

return c._start(this, global_optargs);

where he is able call the private method `c._start` from outside the object itself! Now even if I implemented the `Connection` interface, it fails because I didn't implement a private member!!! So long encapsulation and all the other nice OO properties/principals.

Jim Trainor

unread,
May 6, 2015, 4:32:41 PM5/6/15
to mi...@dartlang.org
The class is effectively sealed ...but it doesn't look intentional.  I think we can call this an "anti pattern". Another way to "seal" a class is to provide no public generative constructors.  If done with the no-public-generative-ctor approach you would have discovered sooner that you can't extend it.

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Bob Nystrom

unread,
May 6, 2015, 4:47:09 PM5/6/15
to General Dart Discussion
On Wed, May 6, 2015 at 1:20 PM, Cristian Garcia <cgarc...@gmail.com> wrote:
I am faced with this 3rd party code

Future run(Connection c, [global_optargs]){
 
if(c == null)
 
throw new RqlDriverError("RqlQuery.run must be given a connection to run on.");
 
if(global_optargs == null)
 global_optargs
= {};
 
return c._start(this, global_optargs);
}

where `Connection` is a custom classes defined in that 3rd party library and I wanted to implement that interface (I was basically just creating a proxy object). Now notice the last line

return c._start(this, global_optargs);

where he is able call the private method `c._start` from outside the object itself!

You could do the same thing in C++, Java, and C#. Privacy isn't object-based in Dart, it's library based. Imagine that the leading "_" is replaced with the name of the library and you'll have a rough intuition about how it works.
 
Now even if I implemented the `Connection` interface, it fails because I didn't implement a private member!!! So long encapsulation and all the other nice OO properties/principals.

Right. In general, if you have a public API that accepts an object, you can't 100% reliably call private methods on it because of this case. It's up to the library designer to either:
  1. Document that Connection is "sealed" and the library doesn't support custom implementations of it.
  2. Avoid calling any private methods on it until it's verified that the object is one of its own known concrete types.
Being able to implement implicit interfaces in Dart is a really cool, useful feature, but it's not a silver bullet. You can still write Dart code that isn't as flexible and injectable as you might want.

Cheers!

- bob


Bob Nystrom

unread,
May 6, 2015, 4:47:41 PM5/6/15
to General Dart Discussion

On Wed, May 6, 2015 at 1:32 PM, Jim Trainor <jim.train...@gmail.com> wrote:
Another way to "seal" a class is to provide no public generative constructors.  If done with the no-public-generative-ctor approach you would have discovered sooner that you can't extend it.

In this case that wouldn't help. Even if you hide the constructors, you can still implement the class, you just can't extend it.

Cheers!

- bob

Rat Jo

unread,
May 6, 2015, 5:16:30 PM5/6/15
to mi...@dartlang.org
I proposed to remove private variable and I'm still for it. Private variable is the copyright of language and should be eradicated. See more discussion about it here: https://groups.google.com/a/dartlang.org/forum/?fromgroups#!mydiscussions/misc/7EAgg5IiFpQ

Cristian Garcia

unread,
May 6, 2015, 5:55:29 PM5/6/15
to mi...@dartlang.org
Bob,
 
You could do the same thing in C++, Java, and C#. Privacy isn't object-based in Dart, it's library based. Imagine that the leading "_" is replaced with the name of the library and you'll have a rough intuition about how it works.

Not sure what you mean,here is a C# toy example, if you try to compile it you'll see that it does NOT let you access private members of one class within some method of another, even if both were defined in he same namespace.

namespace MyProg
{
    class Program
    {
        static void Main() 
        {
            var objA = new A();
            var objB = new B();
            
            objA.a = "Hello, World!";
            
            objB.print (objA);
        }
        
        
    }
    
    public class A 
    {
        private String a;
    }
    
    public class B 
    {
        public void print (A obj) 
        {
            Console.WriteLine(obj.a);
        }
    }
}

I think this behavior is a ugly, it goes against Dart's principals that every class is an interface. Its certainly not in accordance with the Smalltalk philosophy. I am amazed Gilad let this slip into Dart, he was very emphatic about hidden state, messages, interfaces in a couple of videos I've seen.

I guess eliminating this is impossible since its like a super breaking change. Personally I didn't know it was possible, I vow to never use it, and hope people avoid it because it becomes a pain for others.


Cristian Garcia

unread,
May 6, 2015, 6:09:04 PM5/6/15
to mi...@dartlang.org
Bob,

Managed to reproduce what you said like this

public class A 
    {
        
        private String a;
        
        public A (String a)
        {
            this.a = a;
        }
    
        
        public void print (A obj) 
        {
            Console.WriteLine(obj.a);
        }
    }


In C# it only works for an object of the same class, but not for another object of the same library. I am passing my case I am passing my implementation to an object of another class of that 3rd part library.

I think the whole idea if accessing other object's private members is terrible, I am very shocked, even with the more limited C# case.

Bob Nystrom

unread,
May 6, 2015, 6:28:51 PM5/6/15
to General Dart Discussion
On Wed, May 6, 2015 at 3:09 PM, Cristian Garcia <cgarc...@gmail.com> wrote:
Managed to reproduce what you said like this

public class A 
    {
        
        private String a;
        
        public A (String a)
        {
            this.a = a;
        }
    
        
        public void print (A obj) 
        {
            Console.WriteLine(obj.a);
        }
    }


In C# it only works for an object of the same class, but not for another object of the same library.

Sorry, I should have clarified that privacy is class-based in C# since everything is in a class in C#. (You can get something more similar to Dart using "internal".) Class-based privacy doesn't make sense for Dart since you can define things outside of classes.
 
I am passing my case I am passing my implementation to an object of another class of that 3rd part library.

I think the whole idea if accessing other object's private members is terrible, I am very shocked, even with the more limited C# case.

Welcome to most OOP languages. :)

Smalltalk is the only language I know of with object privacy and it was notoriously annoying there.

You have to consider what privacy is for. It's an encapsulation tool to help program maintainers avoid stepping on each other's code. It lets you control which portions of the source code can interfere with each other. By making something private, you say "no code outside of this class/library/whatever can affect this."

Note that this is all textual—it's about the code. It's not about the objects in memory. We programmers maintain code, not in-memory objects. :)

Given that, class-based privacy makes total sense. Two instances of the same class come from the exact same source code. If they couldn't access each other's private state, what encapsulation benefit would you get? You'd just be saying, "this code can't interfere with... itself". The affected code is all in the same class. There's no point in encapsulating within that.

(Of course, this is all totally different from privacy which is used for security, as in object capabilities. Privacy in Dart, C++, C#, Java, etc. is not used for security.)

Cheers!

- bob


Gilad Bracha

unread,
May 6, 2015, 6:53:30 PM5/6/15
to General Dart Discussion
There is a basic tension between abstract data types (ADTs) and interfaces.  The example is a manifestation of that. 

Dart allows any class to be used as an interface. That means we don't need a separate concept of interface,  it means you never have to decide whether to define a type as abstract class or an interface, and it means you don't need to duplicate the type hierarchy if you want to adhere to an interface discipline.

Alas, interfaces really fit best with object-based encapsulation.  However, true object-based encapsulation is an austere discipline that is almost never found in familiar programming languages.  

Tangent: Even Smalltalk does not really implement it; in Smalltalk, all fields are encapsulated, but all methods are public - which is why it is not at all annoying (contrary to what Bob said) but also not at all secure. Implementing right is an open design question; I'd argue that we have an answer for that in Newspeak, but that is actually still being researched.

Hence Dart does not implement object encapsulation. Like most mainstream languages it implements something like ADTs.
ADTs encapsulate on a per type basis.  This always has inconsistencies (the issues in Java were legion, leading to papers, patents and a long, long bug tail). 

In Dart, what happens is that if a type has private members, it can only be implemented outside its library if access to the private member is restricted to *this*.  That is, the code assumes object-encapsulation. That way, no one ever attempt to access the private member on another instance, and in particular and instance that could come from an unrelated implementation. You can define an ADT, but then you cannot implement the type outside the abstraction - it just isn't something ADTs can do.

Dart lets you work either way.  I discussed this in my very first presentation about Dart, at OOPSLA 2011. It's not new, and has not been a big issue in practice. It isn't pretty though.

Reply all
Reply to author
Forward
0 new messages