对原始问题的大量修改:现在我预先呈现整个代码,而不显示解释我动机的变体。为混乱道歉。
我需要一个简单的类型类在类型的成员类型之一上实现投影 - 出于本示例的目的,我们让它成为一个简单的转换:
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
则应使用 for ,最后一个表达式评估为Adapter[Box["F"]]
。如果我 declaer 这很好用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
并且没有隐含,除了Projection[Subject]
将从未-Xlog-implicits
删节问题的编译器日志中搜索(它具有较大的主题层次结构和更多隐含的投影声明)。
然后我尝试了一个中间逆变隐式的技巧,它有时有效:
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
into Projection
,但它也没有改变:
class Projection[-S <: T, T] { /* as before */ }
我的预感是它可能是可行的,但需要巧妙地手动引导编译器进行类型推断,现在我没有新的探索途径。