在需要它们的类型的继承者中提供隐式实例

Nik*_*kov 6 scala implicit

令我惊讶的是,我发现编译器无法解析以下示例中的隐含:

trait API {
  def f(implicit a: Int) = ???
}

class Impl extends API {
  implicit val int = 2
}

(new Impl).f
Run Code Online (Sandbox Code Playgroud)

以及在

class Impl extends API
object Impl {
  implicit val int = 2
}
Run Code Online (Sandbox Code Playgroud)

我对此深感沮丧.为什么会这样,是否有解决方法?我必须注意,将隐式导入到外部作用域不是一个选项,因为上述的唯一目的是隐藏用户的实现细节Impl.

以上是API的实现者提供特定类型类实例的模式的摘要,这些实例被API中实现的函数使用.最终用户使用这些实现.

Nik*_*kov 0

好的。是可以解决的。问题显然在于隐式解析范围。我们需要以某种方式向其中添加隐式声明。根据这个答案,范围之一是类型参数的隐式范围。所以我们要做的就是全力以赴。

显然我们不能对 type 的隐式作用域做任何事情Int,但是我们可以对我们定义的类型的隐式作用域做任何事情。因此,要解决上述问题,技巧是使用幻像类型来确定类型类的实例,这将提供我们所需的内容。

// 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 )
Run Code Online (Sandbox Code Playgroud)

这种方法是可扩展的,可以解决更多涉及类型类的情况,但是您需要这些类型类的修改版本,例如Show[a],您需要而不是Show[instancesProvider, a]


这个模式有什么用呢?

下面是一个示例,说明如何在即将推出的SORM 0.4 中使用它来检查类型级别上的某些驱动程序对某些类型的某些操作的支持:

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
Run Code Online (Sandbox Code Playgroud)