强制依赖返回类型必须实现typeclass

问题描述

我正在尝试执行一个规则,即类型类的(依赖)返回类型本身必须实现一个类型类。因此,当用户实现下面的Isvec类型类时,他们还必须确保getElem方法的返回值实现另一个类型类(IsvecElem)。我试图使这项工作看起来像这样:

// A typeclass for an vector element
abstract class IsvecElem[A,T: Numeric] {
  def dataOnly(self: A): T
}
// A typeclass for a vector
abstract class Isvec[A,T: Numeric] {
  // I would like this OutElem output type to implement the IsvecElem typeclass
  protected type OutElem
  def getElem(self: A,i: Int)(implicit tcO: IsvecElem[OutElem,T]): OutElem
}
// give this typeclass method Syntax
implicit class Isvecops[A,T: Numeric](value: A) {
  def getElem(i: Int)(implicit tcA: Isvec[A,T],tcO: IsvecElem[tcA.OutElem,T]) = tcA.getElem(value,i)
}

问题与Isvecops中的getElem一起出现-这引发了编译错误

type OutElem in class Isvec cannot be accessed as a member of Isvec[A,T] from class Isvecops. Access to protected type OutElem not permitted because enclosing class Isvecops is not a subclass of class Isvec

拥有Isvecops扩展Isvec并不是一个立即的解决方案,也不是应该的,所以我想知道代码中其他地方的方法是否有错误

非常感谢任何帮助。

解决方法

IsVecOps不应扩展IsVec。隐式类(仅旨在引入扩展方法)扩展类型类将非常奇怪。

如果暂时删除访问修饰符(protected),您会看到错误消息更改为

illegal dependent method type: parameter may only be referenced in a subsequent parameter section
    def getElem...

尝试添加类型参数(OE)并指定类型细化(IsVec[A,T] { ... }

implicit class IsVecOps[A,T: Numeric](value: A) {
  def getElem[OE](i: Int)(implicit tcA: IsVec[A,T] { type OutElem = OE },tcO: IsVecElem[OE,T]): OE = tcA.getElem(value,i)
}

如果您介绍Aux

object IsVec {
  type Aux[A,T,OE] = IsVec[A,T] { type OutElem = OE }
}

然后您可以更紧凑地重写类型细化

implicit class IsVecOps[A,T: Numeric](value: A) {
  def getElem[OutElem](i: Int)(implicit tcA: IsVec.Aux[A,OutElem],tcO: IsVecElem[OutElem,T]): OutElem = tcA.getElem(value,i)
}

How can I have a method parameter with type dependent on an implicit parameter?

When are dependent types needed in Shapeless?

Why is the Aux technique required for type-level computations?

Understanding the Aux pattern in Scala Type System

Dotty中,您将能够使用特征参数,扩展方法,多个隐式参数列表,同一参数列表中彼此依赖的参数类型:

trait IsVecElem[A,T: Numeric] {
  def dataOnly(self: A): T
}
  
trait IsVec[A,T: Numeric] {
  protected type OutElem
  def (self: A) getElem(i: Int)(using IsVecElem[OutElem,T]): OutElem
}

trait IsVecElem[A,T: Numeric] {
  def dataOnly(self: A): T
}

trait IsVec[A,T: Numeric] {
  /*protected*/ type OutElem
  def getElem(self: A,i: Int)(using IsVecElem[OutElem,T]): OutElem
}
extension [A,T: Numeric](value: A) {
  def getElem(i: Int)(using tcA: IsVec[A,T],tcO: isVecElem[tcA.OutElem,T]) = tcA.getElem(value,i)
}

(在0.28.0-bin-20200908-ce48f5a-NIGHTLY中测试)