首页 > 解决方案 > 在 scala 2.13 中,如何隐式使用[value singleton type]?

问题描述

以下简单代码:

  implicit val a: String = "abc"

  implicitly[a.type]

尽管a完全在范围内并且类型一致,但仍无法编译:

Error:(9, 13) could not find implicit value for parameter e: 
...AP.a.type with Singleton
  implicitly[a.type with Singleton]

这种不一致的行为似乎是故意的。这个设计的意义何在?我能做的最短的改变是什么让它编译?

更新1:我刚刚意识到注释“String”是罪魁祸首,以下代码总有效:

  val a: String = "abc"

  implicit val aa: a.type = a

  implicitly[a.type]

不幸的是它包含很多重复的定义,有没有机会让它更短?

非常感谢你的帮助。

标签: scalatypesimplicitsubtyping

解决方案


尽管a完全在范围内并且类型一致,但仍无法编译:

它的类型不一致。

考虑例子

trait Parent
trait Child extends Parent

{
  implicit val p: Parent = null
  // implicitly[Child] // doesn't compile
}

{
  implicit val c: Child = null
  implicitly[Parent] // compiles
}

同样,在我们的案例a.type <: String中,您声明了类型的隐式String,因此a.type找不到类型的隐式。

如果您有某种类型的隐式,它也适用于所有超类型,但不适用于所有子类型(严格)。这只是里氏原理。这就是为什么你不应该寻找类型的隐式Any定义类型的隐式Nothing

类似地,如果一个类型类是协变的,那么这个类型类的一个实例的所有超类型也是它的实例

trait TC[+A]

{
  implicit val inst: TC[Parent] = null
  // implicitly[TC[Child]] // doesn't compile
}

{
  implicit val inst: TC[Child] = null
  implicitly[TC[Parent]] // compiles
}

如果一个类型类是逆变的,那么这个类型类的一个实例的所有子类型也是它的实例

trait TC1[-A]

{
  implicit val inst: TC1[Parent] = null
  implicitly[TC1[Child]] // compiles
}

{
  implicit val inst: TC1[Child] = null
  // implicitly[TC1[Parent]] // doesn't compile
}

显然,对于不变类型类没有这样的属性。

我能做的最短的改变是什么让它编译?

它不应该编译。

更新1:我刚刚意识到注释“字符串”是罪魁祸首,以下代码总有效

确实如此。您定义了类型a.type的隐式,因此找到了这种类型a.type的隐式。

如果您正在寻找超类型的隐含,您可以这样做

def implicitSupertypeOf[A] = new PartiallyAppliedImplicitSupertypeOf[A]
class PartiallyAppliedImplicitSupertypeOf[A] {
  def apply[B]()(implicit b: B, ev: A <:< B): B = b
  // by the way, the following will not work: 
  //    def apply[B]()(implicit ev: A <:< B, b: B): B = b
  //    def apply[B >: A]()(implicit b: B): B = b  
}

import Predef.{$conforms => _, _}

{
  implicit val p: Parent = null
  implicitSupertypeOf[Child]() //compiles
}

{
  implicit val inst: TC[Parent] = null
  implicitSupertypeOf[TC[Child]]() //compiles
}

{
  implicit val inst: TC1[Child] = null
  implicitSupertypeOf[TC1[Parent]]() //compiles
}

{
  implicit val a: String = "abc"
  implicitSupertypeOf[a.type]() //compiles
  implicitSupertypeOf["abc"]() //compiles
}

从上面可以看出,定义没有意义,implicitSubtypeOf[A]()因为它的行为应该像 standard implicitly[A]

顺便说一句,我们还可以修改 的行为,implicitly以便它只接受没有子类型的确切类型

def implicitExactTypeOf[A] = new PartiallyAppliedImplicitExactTypeOf[A]
class PartiallyAppliedImplicitExactTypeOf[A] {
  def apply[B <: A]()(implicit b: B, ev: A =:= B) = b
}

{
  implicit val p: Parent = null
  // implicitExactTypeOf[Child]() // doesn't compile
  implicitExactTypeOf[Parent]() // compiles
}

{
  implicit val c: Child = null
  implicitExactTypeOf[Child]() // compiles
  // implicitExactTypeOf[Parent]() // doesn't compile
}

{
  implicit val inst: TC[Parent] = null
  // implicitExactTypeOf[TC[Child]]() // doesn't compile
  implicitExactTypeOf[TC[Parent]]() //compiles
}

{
  implicit val inst: TC1[Child] = null
  implicitExactTypeOf[TC1[Child]]() //compiles
  // implicitExactTypeOf[TC1[Parent]]() // doesn't compile
}

{
  implicit val a: String = "abc"
  implicitExactTypeOf[String]() // compiles
  // implicitExactTypeOf["abc"]() // doesn't compile
  // implicitExactTypeOf[a.type]() // doesn't compile
}

我们也可以实现implicitStrictSupertypeOf(接受超类型但不接受类型本身),implicitStrictSubtypeOf(如implicitly接受子类型但不接受类型本身)。


实际上,在与@HTNW讨论之后,我想我理解了你的观点。所以我们应该说编译器不喜欢召唤单例。


推荐阅读