Okay. It's solvable. The problem obviously is with the implicit resolution scope. We need to somehow add our implicit declaration to it. According to this answer one of the scopes is Implicit scope of type arguments. So what we'll do is muscle our way into it.
We obviously can't do anything with the implicit scopes of the type Int
, but we can with the ones of types that we define. So to solve the stated problem the trick is to use a phantom type to determine an instance of a typeclass, which will provide what we need.
// The typeclass, which we'll resolve implicitly.
trait IntResolver[ instancesProvider ]{ val int: Int }
// Notice the `instancesProvider` parameter.
trait Trait[ instancesProvider ] {
def f( implicit intResolver: IntResolver[ instancesProvider ] ) =
intResolver.int * 2
}
// We specify `Impl` as a type-parameter to `Trait` to explicitly state
// that the compiler should include the implicit scope of `Impl` in its search
// for instances.
class Impl extends Trait[ Impl ]
object Impl {
implicit val intResolver = new IntResolver[ Impl ] { val int = 4 }
}
assert( (new Impl).f == 8 )
This approach is scalable to solve more involved cases with typeclasses, but you'll need modified versions of those typeclasses, e.g. instead of Show[a]
, you'll need Show[instancesProvider, a]
.
What's this pattern useful for?
Here's an example in the spirit of how it'll be used in the coming SORM 0.4 to check for support of certain operations on certain types by certain drivers on a type-level:
trait API[ driver ]{
def regex
[ ref, value ]
( ref: ref, value: value )
( implicit compiler: RegexCompiler[ driver, ref, value ] )
= sys.error("The function is implemeneted here")
}
// A typeclass with ops, that don't matter in this example
trait RegexCompiler[ driver, ref, value ]
class Mysql extends API[ Mysql ]
object Mysql {
implicit def stringRegexCompiler[ ref ] = new RegexCompiler[ Mysql, ref, String ] {}
}
class CouchDB extends API[ CouchDB ]
object CouchDB {
// This driver has no support for regex, so there's no instance
}
(new Mysql).regex("someref", "a") // compiles fine
(new Mysql).regex("someref", 2) // doesn't compile
(new CouchDB).regex("someref", "a") // doesn't compile