如何使两个不同特征的依赖类型被识别为同一类型
How to make it so that dependent types in two different traits are recognized as the same type
我 运行 遇到一个问题,我正在使用多个使用依赖类型的特征,但是当我尝试在我的业务逻辑中组合这些特征时,出现编译错误。
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
}
processor
中的代码将无法编译,因为 extractData
需要 ConfLoader.T
类型的输入,但 conf.settings
是 Conf.T
类型的输入。这些是不同的类型。
但是,我已经指定两者都必须是 MySettings
的子类,所以我应该可以在需要另一个的地方使用一个。我知道 Scala 不编译代码,但是有一些解决方法可以将 conf.settings
传递给 confLoader.extractData
?
===
我想报告一下,对于我上面编写的代码,有一种编写它的方法可以减少我对依赖类型的使用。我今天在试验 Traits 时注意到,Scala 支持对实现 Trait 的 类 上的 defs 和 vals 进行子类化。所以我只需要为 extractData
的参数创建一个依赖类型,而不是输出。
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.asInstanceOf[confLoader.T])
}
trait Data
case class RealData(anInt: Int, aDouble: Double) extends Data
trait MySettings
case class RealSettings(data: RealData) extends MySettings
trait Conf {
def id: UUID
def settings: MySettings
}
case class RealConf(id: UUID, settings: RealSettings) extends Conf
trait ConfLoader{
type T <: MySettings
def extractData(settings: T): Data
}
case class RealConfLoader(someInfo: Int) extends ConfLoader {
type T = RealSettings
override def extractData(settings: RealSettings): RealData = settings.data
}
上面的代码做了同样的事情,减少了对依赖类型的依赖。我只从代码中删除了 processor
。 processor的实现可以参考下面任意一个方案。
The code in processor
will not compile because extractData
expects input of type ConfLoader.T
, but conf.settings
is of type Conf.T
. Those are different types.
在方法processor
中你应该指定这些类型是相同的。
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)
恕我直言,如果您不需要依赖类型提供的任何功能,您应该只使用普通的类型参数.
因此:
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)
我 运行 遇到一个问题,我正在使用多个使用依赖类型的特征,但是当我尝试在我的业务逻辑中组合这些特征时,出现编译错误。
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
}
processor
中的代码将无法编译,因为 extractData
需要 ConfLoader.T
类型的输入,但 conf.settings
是 Conf.T
类型的输入。这些是不同的类型。
但是,我已经指定两者都必须是 MySettings
的子类,所以我应该可以在需要另一个的地方使用一个。我知道 Scala 不编译代码,但是有一些解决方法可以将 conf.settings
传递给 confLoader.extractData
?
===
我想报告一下,对于我上面编写的代码,有一种编写它的方法可以减少我对依赖类型的使用。我今天在试验 Traits 时注意到,Scala 支持对实现 Trait 的 类 上的 defs 和 vals 进行子类化。所以我只需要为 extractData
的参数创建一个依赖类型,而不是输出。
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.asInstanceOf[confLoader.T])
}
trait Data
case class RealData(anInt: Int, aDouble: Double) extends Data
trait MySettings
case class RealSettings(data: RealData) extends MySettings
trait Conf {
def id: UUID
def settings: MySettings
}
case class RealConf(id: UUID, settings: RealSettings) extends Conf
trait ConfLoader{
type T <: MySettings
def extractData(settings: T): Data
}
case class RealConfLoader(someInfo: Int) extends ConfLoader {
type T = RealSettings
override def extractData(settings: RealSettings): RealData = settings.data
}
上面的代码做了同样的事情,减少了对依赖类型的依赖。我只从代码中删除了 processor
。 processor的实现可以参考下面任意一个方案。
The code in
processor
will not compile becauseextractData
expects input of typeConfLoader.T
, butconf.settings
is of typeConf.T
. Those are different types.
在方法processor
中你应该指定这些类型是相同的。
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)
恕我直言,如果您不需要依赖类型提供的任何功能,您应该只使用普通的类型参数.
因此:
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)