Change `private` to mean `private[this]`?

@som-snytt brought up the idea of changing the meaning of private to be what is today private[this].

This would mean that a private member can only be accessed through a this prefix (or O.this).
The following is allowed today, but would require private[C] after that change:

class C { private val x = 0; def m(c: C) = c.x }

Intuitively, this seems to make sense for fields (val / var), but I’m not sure for private methods – what do others think? The following would no longer be allowed either:

class C {
  private def prop = ...
  def foo(c: C) = if (this.prop compare c.prop) ...
}

Having different meaning for private def and private val is not an option IMO, it goes against the uniform access principle.

Relevant spec: https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#private

Another change that is maybe undesired: today a private member cannot be abstract. If we change the meaning of private to be private[this], users will write private[C], which makes a member “qualified private”. Such a member could then be abstract and can be overridden.

trait T {
  private[T] def f = 0
  trait U extends T { override private[T] def f = 1 } // allowed
}

As an independent improvement, @retronym suggested to change the backend and emit getfield / putfield instead of calling the getters for private fields when in the same class.

6 Likes

Not so independent, I think. I think private[this] is mostly used for performance reasons (JIT can optimize code anyway, but simpler bytecode gets optimized quicker) so if backend is improved to generate simpler bytecode without the use of private[this] then changing private semantics would be less of an issue.

2 Likes

Perhaps some are already aware, but the current behaviour of Scala matches Java’s:

jshell> class C {
   ...>   private final int x;
   ...>   C(int x) { this.x = x; }
   ...>   int m(C c) { return c.x; }
   ...> }
|  modified class C

jshell> var c0 = new C(0); var c1 = new C(1); c0.m(c1)
c0 ==> C@5e3a8624
c1 ==> C@91161c7
$7 ==> 1
3 Likes

I think the backend improvement is definitely worthwhile. Changing the meaning of private seems too disruptive to me. There are a lot of programs that would have to be rewritten.

8 Likes

Yeah, I don’t see any downsides in short-circuiting for private fields (without dropping the accessors, if needed).

2 Likes

this would be great; I have private[this] all over my code and never ever need private without that qualifier, so it’s just noise.

3 Likes

Personally, I doubt it is that disruptive. I think 95% of the cases where private is used, people actually mean and use it like private[this], but don’t want to write the latter because of noise. I also expect you could make an automatic rewrite rule right? And otherwise you get pretty straightforward compiler errors.

Though it is not a game-changer, I would be happy if we could just use private and it would actually be completely private.

3 Likes

It’s hard to be sure, but at least anecdotally I agree – more or less every time I can recall ever seeing private in the wild, the intent has pretty clearly been private[this]. I suspect that intentional use of private as it is actually defined is fairly rare.

I don’t care much about this one way or t’other, but I do suspect this feature is commonly misused anyway…

3 Likes

I’m actually pretty often doing things like that (in a commercial code at work):

import MyType._

class MyType(a: Int) {
  def b = a + x
}

object MyType {
  private val x = 5
}

With private[this] instead of private that wouldn’t work.

1 Like

I think the change (making private mean private[this]) is fine–probably a good idea, even–but I think the impact is being severely underestimated due to people with different coding styles not being represented here.

One place this comes up extensively is in cooperative mutability. Dangerous, to be sure, but also incredibly useful sometimes.

class Doubly[A] private (   // Doesn't even make sense as private[this]
  value: A,
  private var prev: Doubly[A],
  private var next: Doubly[A]
) {
  def addAfter(a: A): Unit = {
    val nx = new Doubly(a, this, next)
    nx.next.prev = nx   // Will NOT work if private[this]!
    next = nx
  }
}

This also comes up when private is used to hide immutable data but the immutable structure needs to report on its (private) implementation in a distributed way.

And it comes up when extracting functionality from a class to its companion object, in both directions: the class needs to call non-public methods in the companion, and the companion needs to call non-public methods in the class.

Now, that isn’t to say that the change is wrong. There’s an argument that private really should be private[this] given that you can always widen the accessibility by using private[Foo].

I just don’t think we should be too sanguine about the scale of the change.

So even though the endpoint is desirable, I tend to agree with Martin that it is too disruptive at this point.

3 Likes

I think this is the place where you should use protected

1 Like

protected is to give access to subclasses. For cooperative mutability, subclassing is perilous (since you rely upon implementation details that the subclass might alter!). So no, that’s a different use case.

The java protected/private is quite surprising. I was astounded that private didn’t mean private[this]. Protected allows access both to classes in the same package as well as subclasses. Very confusing.

There isn’t a valid reason to allow access to a private variable/structure from anything other than [this]. If someone depends on it, the design has a horrible flaw. It’s best to treat them as a simple macro or a function intended to be inlined. I think the weird private definition has something to do with static class methods, but I’m not sure.

Protected methods for subclasses (or implementations) are sensible, iff it is to provide a contract. Never for implementation. The most common case is something like mkClone() method. It can be used by an abstract class/trait, but require the concrete class to create an object of the correct type.

Anything larger uses of them and the design becomes tightly intertwined implementation details. Always a bad idea.

I would like to change the definition as you suggest. But I agree with others, there is too many codes that would break if it were changed. Note that it would also be a compile time only restriction. The jvm would still use the original definitions.

can’t this be handled by ScalaFix?

remember anyone moving their existing code base from Scala 2.13 to Scala 3 would be making a conscious decision. and how common is the private only usage?

2 Likes

Is that to avoid the accessor methods, or because you actually want to enforce the restricted visibility?

I expect 99% of times when people write private[this] they do it to avoid the accessors. Plain private restricts the visibility enough to keep everything in control of the author. A private member can only accessed within the same file, and changing it is binary compatible.

So if we improve the backend to avoid going through the accessors, maybe even skip emitting them in cases when they are not necessary, that should address the common complaint.

1 Like

yes, that. Code is less efficient if you don’t add [this] to vals (AFAIK). They don’t become actual fields in the byte code, so you pay indirection for every access to a private val.

1 Like

Dug out a 2011 thread: [scala-language] Why are getters/setter methods generated for private val/var? | The Scala Programming Language

Cay Horstmann: I can’t figure out why private val/var fields need getter/setter methods

Martin Odersky: I am not 100% sure anymore

Martin mentions accessing the field from inner classes, but that is also allowed for private[this] fields, and the compiler just name-mangles the field and makes it public in this case.

I’ll give it a try and see what happens if we stop emitting getters and setters for private fields altogether.

3 Likes

I have a vague memory that private[this] caused problems (compiler error or crash) with access from inner classes, closures etc. back in Scala 2.10 (?) but was fixed in 2.11 (?).

Though you might be right and I underestimate the amount this kind of code is actually used. It is, in my humble opinion of course, the wrong default to use private instead of private[Foo] in such cases. Also I still think that it doesn’t have to be that disruptive:

  • The errors produced are very straightforward. You can just click them and simply add the scope necessary.
  • There might be a possibility to use rewrite rules that automatically insert the scope of the object if private is used. I am not sure if this is actually possible though.
  • Maybe we can use a similar trick for strict equality and use a compiler flag that allows private to mean private[Foo]. Though, I am not sure that is feasible either.

I think it is a bit disappointing that with all the breaking changes in Dotty that this is seen as too disruptive, whereas I don’t think we have even checked whether it is, and if there are any measures that might make the migration smoother.

2 Likes

In Java, it almost makes sense. Allowing private static methods allows every instance object to access the static method. Not just [this]. For the instance variables and methods, anyone who uses someone else’s private members needs to be slapped. Scala, has private[this] which is a good thing. It’s mostly an annotation, as the jvm doesn’t support it.

Personally, I use private implementations (or protected object) if the code needs more than a few statements. The ug code is separated from the contract declarations. It’s a hint to the consumers that they really don’t want to know what needs to be done to implement the method. In other places, I’m just moving a {} block out of the method to simplify understanding the code.

Making it a jsr call allows the Hotspot compiler to easily inline the private method. A nice optimization, but one shouldn’t make this kind of choice just for optimization.