Scala 如何使两个不同性状中的依赖类型被识别为同一类型

Scala 如何使两个不同性状中的依赖类型被识别为同一类型,scala,traits,dependent-type,path-dependent-type,type-members,Scala,Traits,Dependent Type,Path Dependent Type,Type Members,我遇到了一个问题,我正在处理几个使用依赖类型的特性,但是当我尝试在业务逻辑中组合这些特性时,我得到了一个编译错误 import java.util.UUID object TestDependentTypes extends App{ val myConf = RealConf(UUID.randomUUID(), RealSettings(RealData(5, 25.0))) RealConfLoader(7).extractData(myConf.settings) } t

我遇到了一个问题,我正在处理几个使用依赖类型的特性,但是当我尝试在业务逻辑中组合这些特性时,我得到了一个编译错误

import java.util.UUID

object TestDependentTypes extends App{
  val myConf = RealConf(UUID.randomUUID(), RealSettings(RealData(5, 25.0)))

  RealConfLoader(7).extractData(myConf.settings)

}

trait Data

case class RealData(anInt: Int, aDouble: Double) extends Data

trait MySettings

case class RealSettings(data: RealData) extends MySettings

trait Conf {
  type T <: MySettings
  def id: UUID
  def settings: T
}

case class RealConf(id: UUID, settings: RealSettings) extends Conf {
  type T = RealSettings
}

trait ConfLoader{
  type T <: MySettings
  type U <: Data
  def extractData(settings: T): U
}

case class RealConfLoader(someInfo: Int) extends ConfLoader {
  type T = RealSettings
  type U = RealData
  override def extractData(settings: RealSettings): RealData = settings.data
}
上面的代码做了同样的事情,并减少了对依赖类型的依赖。我只从代码中删除了处理器。有关处理器的实现,请参阅以下任一解决方案

处理器中的代码将不会编译,因为extractData需要ConfLoader.T类型的输入,但conf.settings的类型是conf.T。这些是不同的类型

在方法处理器中,应指定这些类型相同

def processor(loader: ConfLoader)(conf: Conf)
             (implicit ev: conf.S <:< loader.S): loader.D =
  loader.extractData(conf.settings)
使用类型细化,因为:

def processor[_T](confLoader: ConfLoader { type T = _T }, conf: Conf { type T = _T }) = 
  confLoader.extractData(conf.settings)

处理器中的代码将不会编译,因为extractData需要ConfLoader.T类型的输入,但conf.settings的类型是conf.T。这些是不同的类型

在方法处理器中,应指定这些类型相同

def processor(loader: ConfLoader)(conf: Conf)
             (implicit ev: conf.S <:< loader.S): loader.D =
  loader.extractData(conf.settings)
使用类型细化,因为:

def processor[_T](confLoader: ConfLoader { type T = _T }, conf: Conf { type T = _T }) = 
  confLoader.extractData(conf.settings)


IMHO如果您不需要依赖类型提供的任何功能,您应该只使用普通类型参数

因此:

但是,如果您真的要求它们是类型成员,您可以确保它们是相同的

def processor(loader: ConfLoader)(conf: Conf)
             (implicit ev: conf.S <:< loader.S): loader.D =
  loader.extractData(conf.settings)

IMHO如果您不需要依赖类型提供的任何功能,您应该只使用普通类型参数

因此:

但是,如果您真的要求它们是类型成员,您可以确保它们是相同的

def processor(loader: ConfLoader)(conf: Conf)
             (implicit ev: conf.S <:< loader.S): loader.D =
  loader.extractData(conf.settings)

编译器无法判断这两个值的内部类型是否与当前编写代码的方式相同,因此可以对其进行重构。您真的需要依赖类型吗?为什么一个类型参数,例如trait Conf[T]{…}是不够的?更好的是,你确定你真的想区分设置和数据的子类型吗?我将尝试类型参数的想法。编译器不可能判断这两个值的内部类型与现在编写代码的方式相同,可以通过重构来做到这一点。您真的需要依赖类型吗?为什么一个类型参数,例如trait Conf[T]{…}是不够的?更好的是,你确定你真的想区分设置和数据的子类型吗?我会尝试类型参数的想法。在这一点上,使用AUX模式不是更好吗?@LuisMiguelMejíaSuárez或注解@AUX:@LuisMiguelMejíaSuárez实际上在这个特定的情况下Conf.AUX[S]将非常类似于Conf[S]从你的答案来看。哇,非常酷的库:-是的,正如我在回答中所说的,在这个特定的情况下,我真的看不到使用类型成员有什么好处,这就是为什么我建议改为使用类型参数的原因。@LuisMiguelMejíaSuárez嗯,这取决于使用情况。通常,类型参数是要指定的输入,类型成员是要推断的输出。因此,如果基于MySettings的子类型选择数据的子类型,那么T可以是一个类型参数,U可以是一个类型成员。在这一点上,使用AUX模式不是更好吗?@LuisMiguelMejíaSuárez或注释@AUX:@LuisMiguelMejíaSuárez实际上在这个特定的情况下Conf.AUX[S]将非常类似于Conf[S]从你的答案来看。哇,非常酷的库:-是的,正如我在回答中所说的,在这个特定的情况下,我真的看不到使用类型成员有什么好处,这就是为什么我建议改为使用类型参数的原因。@LuisMiguelMejíaSuárez嗯,这取决于使用情况。通常,类型参数是要指定的输入,类型成员是要推断的输出。例如,如果根据MySettings的子类型选择了数据的子类型,那么T可以是类型参数,U可以是类型成员。