Usually when we write implicit def/vals/classes they are meant to be used implicilty and not directly.
Assume the following API exists in a library Liberation:
package Libration
trait Foo[T]
object Foo {
implicit def ev[T] : Foo[T] = ???
implicit def fromInt(i : Int) : Foo[Int] = ???
protected[Liberation] val protVal : Int = ???
val pubVal : Int = ???
def pubDef : Int = ???
}
The problem
The user gets a cluttered API and is able to access Foo.pubVal, Foo.ev, and Foo.fromInt (not Foo.protVal).
I also want to prevent the user from calling (some) implicit definitions directly, but still enable calling them implicitly to avoid bad usage, e.g.:
implicitly[Foo[Int]] //should compile
val f : Foo[Int] = 1 //should compile
Foo.ev[Int] //if protected should not compile
Foo.fromInt(1) //if protected should not compile
Possible language solution
In similar fashion that we limit protVal access outside the Liberation scope, we can provide syntax that limits implicits access outside the implicit scope, via protected[implicit]. E.g.:
package Libration
trait Foo[T]
object Foo {
protected[implicit] implicit def ev[T] : Foo[T] = ???
protected[implicit] implicit def fromInt(i : Int) : Foo[Int] = ???
protected[Liberation] val protVal : Int = ???
val pubVal : Int = ???
def pubDef : Int = ???
}
Workarounds
As suggested on the scala/scala gitter, it is possible to give the implicits strange names or use a prefix of _, making it hard for the user to call them and push them to the bottom of the auto-completion list. E.g.:
package Libration
trait Foo[T]
object Foo {
implicit def _ev[T] : Foo[T] = ???
implicit def _fromInt(i : Int) : Foo[Int] = ???
protected[Liberation] val protVal : Int = ???
val pubVal : Int = ???
def pubDef : Int = ???
}
As you can see, it looks much nicer with the clean API.
This sounds like a massive workaround to the problem of “I’d like to be able to hide things from users in IntelliJ’s autocompleter”, to which I suggest the better solution of “add support to intellij for ignoring things”.
Worse, implementing this in the scala compiler demands that the work be duplicated in IntelliJ.
Just send a small PR to intellij-scala to allow ignoring implicit methods in completions…
Rockfact: I implemented this feature in NetBeans, back in the day.
Being able to explicitly provide implicits is a core debugging/troubleshooting technique, one which is prevented by this proposal.
We also already have techniques for de-cluttering APIs. Implicits can be put in a dedicated nested Implicits singleton, or defined on a companion, or provided via a package object.
package escapes {
object X {
def x = new Y
}
private[escapes] class Y {
def y = 42
}
}
package escaped {
object Main extends App {
println(escapes.X.x.y)
}
}
The private type seems to leak a member in the same way. The spec on implicit scope doesn’t talk about access, but making the implicit private makes it invisible.
Why isn’t def x = new Y an error “private type Y escape its scope” or
something like that. I’ve gotten errors like that in cases that would seem
to be comparable.