如何在隐词中实现使用站点差异



对原始问题进行了大量编辑:现在我提前展示了整个代码,而没有显示解释我动机的变体。对造成的混乱深表歉意。

我需要一个简单的类型类来实现对类型的成员类型之一的投影——为了这个例子的目的,让我们把它变成一个直接的类型转换:

trait Subject {
type E
type Const 
}
object Subject {
implicit def projection :Projection[Subject] { type Project[X] = Subject { type E = X } } = ???
}
abstract class Projection[S <: Subject] {
type Project[X] <: Subject { type E = X }
}
implicit class ProjectSubject[S <: Subject](private val self :S) extends AnyVal {
def project[X](implicit p :Projection[S]) :p.Project[X] = ???
}
class Box[X] extends Subject { type E = X }
object Box {
implicit def projection[A] :Projection[Box[A]] { type Project[X] = Box[X] } = ???
}
class Adapter[S <: Subject] extends Subject { type E = S#E }
object Adapter {
implicit def adapterProjection[S <: Subject](implicit p :Projection[S])
:Projection[Adapter[S]] { type Project[X] = Adapter[p.Project[X]] } = ???
}
val res = new Adapter[Box["E"]].project["F"]

在上面的例子中,很明显,投影应该是递归的,Subject子类声明它们自己的规则。显然,我希望投影在效果上是相反的:

class Specific extends Adapter[Box["E"]]
val spec = (new Specific).project["F"] //doesn't compile

如果Specific不提供自己的投影,则应使用Adapter的投影,最后一个表达式的计算结果为Adapter[Box["F"]]。如果我解密Projection[-S <: Subject],这会很好地工作,但问题是我需要投影来保留一些属性,这里表示为Const成员类型:

class Projection[S <: Subject] { 
type Project[X] <: Subject { type E = X; type Const = S#Const }
}

为了清楚起见,我从上面的代码中删除了这个约束,因为它不会导致问题。

在前面的示例中,编译器将抱怨缺少隐式Projection[Specific],而不尝试上转换该值。如何使其与使用站点的差异进行编译?

不与存在:

implicit class ProjectSubject[S <: Subject](private val self :S) extends AnyVal {
def project[X](implicit p :Projection[_ >: S <: Subject]) = ???
}

我的猜测是,这里的通配符等效于Subject,并且不会从未桥接问题的编译器-Xlog-implicits日志中搜索到除Projection[Subject]之外的隐含词(该问题具有较大的Subject层次结构,具有更多的隐含投影声明(。

然后,我尝试了一个隐含的中间反变体,它有时有效:

abstract class ProjectionAvailable[-S <: T, T <: Subject] //extends (S => T)
implicit def ProjectionAvailable[S <: Subject](implicit p :Projection[S]) :ProjectionAvailable[S, S] = ??? //(s :S) => s
implicit def ProjectionSubject[S <: T, T <: Subject](s :S)(implicit witness :ProjectionAvailable[S, T]) =
new ProjectionSubject[T](s)
class ProjectionSubject[S <: Subject](private val self :S) extends AnyVal {
def project[X](implicit p :Projection[S]) :p.Project[X] = p.asInstanceOf[p.Project[X]]
}

这看起来很有希望,但不幸的是,编译器和以前一样:查看可用的隐式,将类型参数实例化为ProjectionAvailable[Specific, T],并抱怨缺少Projection,而没有利用其逆变。我尝试了的变体

class ProjectionAvailable[S <: T, T <: Subject]

除了更明显的误差之外没有任何实际的差异。我试着整合ProjectionAvailable变成了Projection,但它也没有改变:

class Projection[-S <: T, T] { /* as before */ }

我的直觉是,这可能是可行的,但需要在类型推理中巧妙地指导编译器,目前我还没有新的探索途径。

我无法重现您提到的行为(这就是为什么我在评论中问您如何测试def project[X](implicit p :Projection[_ >: S <: Subject]) = ???或使用ProjectionAvailable的方法对您不起作用(。

用你在ProjectSubject中使用存在Projection的方法,我额外定义了Projection[Specific],代码编译时不会出现错误

Error: ambiguous implicit values:
both value specificProjection in object App of type App.Projection[App.Specific]{type Project[X] = App.Specific}
and method adapterProjection in object App of type [S <: App.Subject](implicit p: App.Projection[S]): App.Projection[App.Adapter[S]]{type Project[X] = App.Adapter[p.Project[X]]}
match expected type App.Projection[_ >: App.Specific <: App.Subject]
val spec = (new Specific).project["F"]

所以Projection[Specific]的隐式是候选的,我看不出下面的内容怎么会是真正的

这里的通配符等效于Subject,并且不会搜索除Projection[Subject]之外的隐词。

如果我使adapterProjection的优先级低于我的附加隐式Projection[Specific],则

println(scala.reflect.runtime.universe.reify{
(new Specific).project["F"]
}.tree)

打印

App.this.ProjectSubject(new App.this.Specific()).project["F".type](App.this.Implicits.specificProjection)

所以选择的是CCD_ 25。

Scala 2.13.3。

https://scastie.scala-lang.org/Ts9UOx0aSfWuQJNOoVnSAA

原始反变体Projection(没有类型Const(和第一个ProjectSubject(具有非存在Projection(的行为是相同的。

(我在scala 2.13中的答案是,如何隐式使用[value singleton type]?可能是相关的。(

顺便说一句,对于不变的ProjectionProjectionAvailable,我不必对隐词进行优先级排序,并且选择了Projection[Specific]

https://scastie.scala-lang.org/nePYqjKGSWm8IRGLmYCwAA

当我没有定义额外的隐含Projection[Specific]时,你用存在Projection的方法似乎有效,选择了adapterProjection。这种行为怎么了?

https://scastie.scala-lang.org/GiLoerYgT0OtxKyechezvA

相关内容

  • 没有找到相关文章

最新更新