在运行时重新建立路径依赖类型与其父类之间的链接?

时间:2014-12-31 20:31:11

标签: scala path-dependent-type

我正在尝试路径依赖类型。在我的简单示例中,我使用Currency对象来确保Money计算只能在相同货币的Money上执行。

// Simple currency class
case class Currency(code: String, name: String, symbol: String) {

  // Money amounts in this currency
  // Operations are only supported on money of the same currency
  case class Money(amount: BigDecimal) {
    override def toString: String = s"$code $amount"

    val currency: Currency.this.type = Currency.this

    def +(rhs: Money): Money = Money(amount + rhs.amount)

    def -(rhs: Money): Money = Money(amount - rhs.amount)
  }
}

在repl中使用上述类简单计算是直截了当的。

val e1 = Euro.Money(5)
val e2 = Euro.Money(9)
e1 + e2 // Compiles fine

val d1 = Dollar.Money(6)
d1 + e2 // Doesn't compile as expected

这些很简单,因为编译器可以很容易地证明e1和e2共享一种通用货币。然而,当我从文件或数据库中读取金额列表时,证明货币实例共享共同货币要困难得多。例如,我无法看到如何实现下面的整理功能。

trait CurrencyAndMonies {
  val currency: Currency
  val monies: List[currency.Money]
}

// Take a list of money in different currencies and group them by currency
// so their shared Currency type is available to static type checking
// in further calculations
def collate(Seq[Currency#Money]): List[CurrencyAndMonies] = ???

是否可以根据货币对货币进行分类并重新建立它们之间的联系?如果是这样怎么样?我不介意更改签名或从数据库中读取Money金额的方式。

2 个答案:

答案 0 :(得分:1)

你可以垂头丧气:

new CurrencyAndMonies {
  val currency = foo
  val monies = bars.map(_.asInstanceOf[currency.Money])
}

Money#currency分组。

向下转换不是运行时检查的,因此您必须确保货币值具有正确的货币(您已通过货币分组),但它将进行编译。

答案 1 :(得分:1)

在您的示例中键入签名

   def collate(Seq[Currency#Money]): List[CurrencyAndMonies] 

并不要求所有的钱都来自同一种货币,它可以是来自任何货币的随机货币。

  val Euro = Currency("EUR", "Euro", "EUR")
  val USD = Currency("USD", "Dollar", "$")

  def collateOld(s: List[Currency#Money]): CurrencyAndMonies = ???

  // Compiles successfully -> ERROR
  collateOld(List(USD.Money(10), Euro.Money(20)))

通常,您必须传递货币实例和货币清单。例如,您可以这样做:

  abstract class CurrencyAndMonies(val currency: Currency) {
    type Money
    def monies: List[Money]
  }

  def collate(c: Currency)
             (m: List[c.Money]): CurrencyAndMonies { type Money = c.Money } =
    new CurrencyAndMonies(c) {
      type Money = c.Money
      val monies = m
    }


  collate(Euro)(List(Euro.Money(10), Euro.Money(20)))

奇怪的是,它需要在CurrencyAndMonies中重新定义类型Money,但我无法理解为什么只是货币。货币不起作用。如果您将构造函数设为私有并且仅整理一种创建类实例的方法,那么您应该使用保证类型安全的方法