Re: [scala-user] Collision between package private and public declarations

140 views
Skip to first unread message

Sébastien Bocq

unread,
Aug 29, 2012, 12:34:08 PM8/29/12
to scala-l...@googlegroups.com, Som Snytt, scala-user


2012/8/29 Som Snytt <som....@gmail.com>
On Wed, Aug 29, 2012 at 1:03 AM, Sébastien Bocq <sebasti...@gmail.com> wrote:


Shouldn't the code below compile since declarations in package `baz` cannot be accessed from `bar` or from `foo`?


The spec 4.7 says a member is importable if it isn't object-private, private[this].

I guess the next question is, if I have a bunch of stuff in a module that I don't want to share, and I also don't want to write private[pkg] everywhere, should I just use an object instead of a package?

Something like:


package object bar {
  type Bar = Int
}

package object baz {
  type Baz = module.Bazz
}

package baz {
  private[baz] object module {
    private[this] type Bar = bar.Bar
    trait Bazz {
      def foo: Bar
    }
  }
}


(moved the thread to scala-language)

A member that is importable but not accessible makes little sense at first glance.

Otherwise, `module` is a nice idea, and it seems to work for what I have in mind, at the cost of having one additional import per subpackage, e.g.:

package object bar {
  type Bar = Int
}

package object baz {
  private[baz] object module {
    type Bar = bar.Bar
  }
}

package baz
package object qux {
  private[qux] object module {
    type Qux = Char
  }
}

package baz
package qux
import baz.module._
import qux.module._
object Blah {
 val b:Bar = 0
 val q:Qux = 'c'
}

--
Sébastien

Paul Phillips

unread,
Aug 29, 2012, 1:27:02 PM8/29/12
to scala-l...@googlegroups.com, Som Snytt
On Wed, Aug 29, 2012 at 9:34 AM, Sébastien Bocq
<sebasti...@gmail.com> wrote:
> A member that is importable but not accessible makes little sense at first
> glance.

See this branch, which fixes the issue (or at least - I await someone
showing otherwise.)

https://github.com/paulp/scala/tree/topic/whats-in-a-name

Paul Phillips

unread,
Aug 29, 2012, 1:28:45 PM8/29/12
to scala-l...@googlegroups.com, Som Snytt
To ffwd to the relevant test: this issues two ambiguity errors with
current trunk, but only one with that branch.

object Bippy {
private class Node
}
class Bippy {
import Bippy._
import scala.xml._

def f(x: Node): String = ??? // ambiguous, because Bippy.Node is accessible
}
class Other {
import Bippy._
import scala.xml._

def f(x: Node): String = ??? // unambiguous, because Bippy.Node is
inaccessible
}

...

t3160ambiguous.scala:8: error: reference to Node is ambiguous;
it is imported twice in the same scope by
import scala.xml._
and import Bippy._
def f(x: Node): String = ??? // ambiguous, because Bippy.Node is accessible
^
one error found

Som Snytt

unread,
Aug 29, 2012, 7:03:22 PM8/29/12
to Paul Phillips, scala-l...@googlegroups.com
Thanks for reminding me of the recent thread, where Jason had pointed out the spec.

That
https://groups.google.com/d/topic/scala-debate/Tt1XnL_JF7k/discussion
was another great thread.  Or good thread anyway.  It would have been a rhetorically great thread if Jason had had the opportunity to respond to Daniel's "(hence the "regardless")" with a "(hence the "irregardless")".

I'm not 100% persuaded by the argument to disambiguate.  More examples besides Node ("What *is* this node anyway?") might be compelling.

The counter-argument is:  We rely on the compiler for various kinds of support, especially where the brain fails, such as variance annotations.  Here the compiler is telling us: "You asked for ambiguous imports, are you sure you didn't really mean the one that isn't accessible?"  Do we mean to reply, "I always know what I'm importing, even wildcardly."

An alternative solution is: "Yes, I really meant to get public API from bar and protected API from baz, please ignore private bar.Foo."  Let me fix that:

import public bar._
import protected baz._

This way, I'm not depending upon accidents of access (such as whether this code has been refactored to another package).

As a lark,
import public bar.Foo
would verify that Foo is public and not private [foob].

Is it true that most Scala code is public by dint of limiting keystrokes; and anything that's not public is made so by the inliner?  But seriously, this use case should interoperate with any future package object module feature; maybe I could say

import package bar._

to mean whatever API is "exported" in package object bar (not including implicits that are protected[bar], for instance).

Sorry for scala-debating on scala-language, I got carried away.

Paul Phillips

unread,
Aug 29, 2012, 7:57:55 PM8/29/12
to Som Snytt, scala-l...@googlegroups.com
On Wed, Aug 29, 2012 at 4:03 PM, Som Snytt <som....@gmail.com> wrote:
> I'm not 100% persuaded by the argument to disambiguate. More examples
> besides Node ("What *is* this node anyway?") might be compelling.
>
> The counter-argument is: We rely on the compiler for various kinds of
> support, especially where the brain fails, such as variance annotations.
> Here the compiler is telling us: "You asked for ambiguous imports, are you
> sure you didn't really mean the one that isn't accessible?" Do we mean to
> reply, "I always know what I'm importing, even wildcardly."

It isn't the impact on the importer which is a problem. Should I be
able to have private things without ruining the namespaces of everyone
who imports from its owner? That's what's at issue here.

package object foo {
private type PrivateToFoo = String
}

In truth I don't care what the person who imported foo._ meant,
whether he meant to import my private types or not. I do care about
whether I can have type PrivateToFoo at all -- which I can't as long
as it's going to create spurious ambiguities.

Som Snytt

unread,
Aug 29, 2012, 8:59:07 PM8/29/12
to Paul Phillips, scala-l...@googlegroups.com
OK, now you're just toying with my capacity for confusion.

On Wed, Aug 29, 2012 at 4:57 PM, Paul Phillips <pa...@improving.org> wrote:

package object foo {
  private type PrivateToFoo = String
}

This is the one that really is private, right?, because a package object is really an object, and private to an object means private[this], object-private. (Conjecture.)


In truth I don't care what the person who imported foo._ meant,
whether he meant to import my private types or not.  I do care about
whether I can have type PrivateToFoo at all -- which I can't as long
as it's going to create spurious ambiguities.

Well, I guess we care if the importer runs into us at Starbucks and starts yelling at us because we added a private class Node.  And maybe he's carrying a concealed weapon.

Otherwise we don't really care.  In terms of a contract, private or protected API is as much APIsh as public API.  There are many potential clients, including the new person on the team adding a method to a companion object with privates exposed. Object-private also matters for variance, so it's clearly a privileged access boundary. (PiS 19.7, "You might wonder whether this code passes the type checker."  I'm still wondering, but I swear that before the day I die, I will know, albeit without ceasing to wonder.)

Maybe import should default to "import public" (though I'm slightly inclined toward the status quo)?  Import non-private seems slightly arbitrary.  It makes me feel that Scala access modifiers and packaging/modularity is deeply different from Java's, and my old expectations about private must change.

package object bar {
  type PrivateToBaz = Int
}

package object baz {
  private type PrivateToBaz = String
}

package baz {
  private case class Biff(b: String)
}

package objprv {

  import bar._
  import baz._

  object Test {
    //val a = Biff()
    val y: PrivateToBaz = 1  // OK?
    def main(args: Array[String]) {
    }
  }
}

Paul Phillips

unread,
Aug 29, 2012, 9:30:44 PM8/29/12
to Som Snytt, scala-l...@googlegroups.com
On Wed, Aug 29, 2012 at 5:59 PM, Som Snytt <som....@gmail.com> wrote:
> On Wed, Aug 29, 2012 at 4:57 PM, Paul Phillips <pa...@improving.org> wrote:
>> package object foo {
>> private type PrivateToFoo = String
>> }
>
> This is the one that really is private, right?, because a package object is
> really an object, and private to an object means private[this],
> object-private. (Conjecture.)

I too enjoy attempting to retrofit rationale to observed behavior, but
this is another helping of bug, one way or another. This is
ambiguous:

object foo { private type Node = Int }
object fooz { type Node = String }
class A {
import fooz._
import foo._
def f(x: Node) = ???
}
./c.scala:10: error: reference to Node is ambiguous;
it is imported twice in the same scope by
import foo._
and import fooz._
def f(x: Node) = ???
^
one error found

... yet if either foo or fooz is a package object instead of a regular
object, it compiles.

Som Snytt

unread,
Aug 30, 2012, 12:29:40 AM8/30/12
to Paul Phillips, scala-l...@googlegroups.com
It all makes sense to me, so, quick, before I forget --

On Wed, Aug 29, 2012 at 6:30 PM, Paul Phillips <pa...@improving.org> wrote:
On Wed, Aug 29, 2012 at 5:59 PM, Som Snytt <som....@gmail.com> wrote:
> On Wed, Aug 29, 2012 at 4:57 PM, Paul Phillips <pa...@improving.org> wrote:
>> package object foo {
>>   private type PrivateToFoo = String
>> }
>
> This is the one that really is private, right?, because a package object is
> really an object, and private to an object means private[this],
> object-private. (Conjecture.)

Sorry, I wasn't actually thinking and I was rushing out to pick up the Kindergartner.  Or is that Kindergärtner?

The (only) difference is that package objects have no notion of companion class.  They don't, do they?  I hope not, because it crashes M7 with an error that breathes frustration:

how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name: top/baz/Biff$

So it makes sense for private to be taken as private[this] in a package object, though it makes import seem irregular for that case.  (I haven't look at the source yet on that.)
 

I too enjoy attempting to retrofit rationale to observed behavior, but
this is another helping of bug, one way or another.  

Wash the bug down with some beer and it's an evening.
 
This is ambiguous:

object foo { private type Node = Int }
object fooz { type Node = String }
class A {
  import fooz._
  import foo._
  def f(x: Node) = ???
}
./c.scala:10: error: reference to Node is ambiguous;
it is imported twice in the same scope by
import foo._
and import fooz._
  def f(x: Node) = ???
           ^
one error found

... yet if either foo or fooz is a package object instead of a regular
object, it compiles.

For me, with package object fooz it's still ambiguous.

Also regular is when extending, import baz._ picks up BazzieImportable, not BazzieOnly.

class Bazzie {
  protected[this] type BazzieOnly = String
  //protected type BazzieImportable = String
}

package object baz extends Bazzie {

  private type PrivateToBaz = String
}

I'll submit a PR to the Scala puzzlers repo.  As usual, it's not puzzling but exceedingly regular.  I'll report if I can't explain something the morning after.

Paul Phillips

unread,
Aug 30, 2012, 9:14:06 PM8/30/12
to scala-l...@googlegroups.com

On Wed, Aug 29, 2012 at 9:29 PM, Som Snytt <som....@gmail.com> wrote:
The (only) difference is that package objects have no notion of companion class.  They don't, do they?

I don't know; it's a place where I've tried without success to obtain clarification.  One can try to derive the answer empirically, and one will wind up with all the answers:

package foo

// this compiles: package object bippy accesses bippy's private x
package object bippy { private val y = 5 ; def f(x: bippy) = println(x.x) }
// this fails: class bippy can't see package object's private y
class bippy { private val x = 1 ; def f = println(bippy.y) }

// ./a.scala:5: error: object y is not a member of package foo.bippy
// class bippy { private val x = 1 ; def f = println(bippy.y) }
//                                                         ^
// one error found

Reply all
Reply to author
Forward
0 new messages