I wonder if this would be a worthwhile construct: A modifier to make a type (class, trait) “transparent”, very much like a non-opaque type alias is transparent. The motivation is to allow easier mixin of implementing traits without them showing up in the formal type of the class that uses the mixin. Kind of the opposite of self-types (trait Impl { self: Public => ... }). Something like (a more or less random example):
trait Ex[A] {
def name: String
}
transparent trait ExImpl[A] {
self: Ex[A] with Product =>
def name: String = productPrefix
}
case class Example() extends Ex[Int] with ExImpl[Int]
A syntactic variant would be
trait Ex[A] {
def name: String
}
trait ExImpl[A] {
self: Ex[A] with Product =>
def name: String = productPrefix
}
case class Example() extends Ex[Int] {
with ExImpl[Int] =>
}
Thus when you look at the type of Example, it would not bear any witness of ExImpl.
My current work around for avoiding the pollution with implementation details is this:
object Example {
def apply(): Example = Impl()
private final case class Impl() extends Example with ExImpl[Int]
}
trait Example extends Ex[Int]
I would rather use a qualifier at usage site than at definition site. And instead of transparent I would just use private, like in C++:
trait Ex[A] {
def name: String
}
trait ExImpl[A] extends Ex[A] {
def name: String = ...
}
case class Example() extends Ex[Int] with private ExImpl[Int]
We could also support protected inheritance.
The current state (always public inheritance) is sometimes problematic (see for instance how the supertypes of List mix implementation details such as StrictOptimizedSeqOps or IterableFactoryDefaults with the rest of the hierarchy).
Oh neat, didn’t know that (know very little C++). Post explaining this; although here it looks like it will make public members private, so I think the use case and semantics are different? Nevertheless, the syntax makes sense.
How would you prevent people from discovering the hidden inheritance via pattern matching? As in Example() match { case _: ExImpl[_] => ... }. If you don’t prevent that, the inheritance isn’t really “private” IMHO, as it’s publicly observable.
I think that if you write with private ExImpl, the compiler has to splice in the body of ExImpl without generating an “interface” (or whatever it would normally do) for ExImpl.
(I don’t know if anything related to the way extends Any works would come handy?)
I think Julien has introduced an idea in this thread quite different from the OP.
I think that if you mixin using A extends private Impl, this should allow Impl to define any API that A formally declares without becoming part of the type signature of A.
So if class Foo extends Ordering[Int], and trait FooImpl { def compare(a: Int, b: Int): Int = if (a < b) -1 else if (a > b) +1 else 0 }, then obviously compare does not become private. I think that would be an extremely rarely needed “transformation”, that is also quite difficult to understand. If you just want to provide “operations {that} should remain hidden implementation details”, then you can already use protected methods, that’s exactly what they are for.
So sorry, but I think the thread no longer follows the original idea.
Do you realize that both compile when the scrutinee is upcasted to Any?
It’s not a problem when matching "foo" (it just won’t match), but it would expose a private inheritance relation when matching ExImpl[_].
It doesn’t have to be Any. It can be any trait up the inheritance DAG from the class with the private inheritance in question!
(Also, IMHO the goal of a type system is to be safe when you use its standard features; you can’t just say a foundational part of it is “unsafe anyways”, and hand-wave problems that way.)