如何使两个不同特征中的依赖类型被识别为同一类型

问题描述 投票:0回答:2

我遇到了一个问题,我正在处理几个使用依赖类型的特质,但是当我尝试将特质结合到我的业务逻辑中时,出现编译错误。

import java.util.UUID

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

  RealConfLoader(7).extractData(myConf.settings)

  def processor(confLoader: ConfLoader, conf: Conf) = confLoader.extractData(conf.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
}

processor中的代码将无法编译,因为extractData期望输入的类型为ConfLoader.T,但是conf.settings的类型为Conf.T。这些是不同的类型。

但是,我已经指定两者都必须是MySettings的子类,因此应该在需要使用另一个的情况下使用这种情况。我了解Scala不会编译代码,但是有一些解决方法可以使conf.settings传递到confLoader.extractData吗?

scala traits dependent-type path-dependent-type type-members
2个回答
3
投票

processor中的代码将无法编译,因为extractData期望输入的类型为ConfLoader.T,但是conf.settings的类型为Conf.T。这些是不同的类型。

在方法processor中,您应指定这些类型相同。

为此使用type refinements:或者

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

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

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

2
投票

恕我直言,如果您不需要依赖类型提供的任何功能,则应只使用普通的类型参数

因此:

trait Conf[S <: MySettings] {
  def id: UUID
  def settings: S
}

final case class RealConf(id: UUID, settings: RealSettings) extends Conf[RealSettings]

trait ConfLoader[S <: MySettings, D <: Data] {
  def extractData(settings: S): D
}

final case class RealConfLoader(someInfo: Int) extends ConfLoader[RealSettings, RealData] {
  override def extractData(settings: RealSettings): RealData =
    settings.data
}

def processor[S <: MySettings, D <: Data](loader: ConfLoader[S, D])(conf: Conf[S]): D =
  loader.extractData(conf.settings)

但是,如果您确实要求他们成为类型成员,则可以确保两者相同。

def processor(loader: ConfLoader)(conf: Conf)
             (implicit ev: conf.S <:< loader.S): loader.D =
  loader.extractData(conf.settings)
© www.soinside.com 2019 - 2024. All rights reserved.