假设我有以下案例类:

case class Product(name: String, categoryId: Option[Long]/*, other fields....*/)

在这里你可以看到 categoryId 是可选的。

现在假设我的 DAO 层中有以下方法:
getCategoryProducts(): List[Product] = {
    // query products that have categoryId defined
}

您会看到,此方法返回的产品是 guaranteed 以使用某个值定义 categoryId。

我想做的是这样的:
trait HasCategory {
    def categoryId_!: Long
}
// and then specify in method signature
getCategoryProducts(): List[Product with HasCategory]

这会起作用,但是这样的产品将有两种方法:categoryId_!categoryId 闻起来很糟糕。

另一种方法是:
sealed trait Product {
    def name: String
    /*other fields*/
}
case class SimpleProduct(name: String, /*, other fields....*/) extends Product
case class ProductWithCategory(name: String, categoryId: Long/*, other fields....*/) extends Product
def getCategoryProducts: List[ProductWithCategory] = ...

这种方法有助于避免方法categoryId 和categoryId_! 的重复,但它需要您创建两个case 类和一个复制所有字段的特征,这也很臭。

我的问题:我怎样才能使用 Scala 类型系统来声明这个没有这些字段重复的特定情况?

最佳答案

一种方法是使用类型类 -

import scala.language.implicitConversions

object Example {

  sealed class CartId[T]

  implicit object CartIdSomeWitness extends CartId[Some[Long]]
  implicit object CartIdNoneWitness extends CartId[None.type]
  implicit object CartIdPresentWitness extends CartId[Long]

  case class Product[T: CartId](name: String, categoryId: T /*, other fields....*/)

  val id: Long = 7

  val withId = Product("dsds", id)
  val withSomeId = Product("dsds", Some(id))
  val withNoneId = Product("dsds", None)

  val presentId: Long = withId.categoryId
  val maybeId: Some[Long] = withSomeId.categoryId
  val noneId: None.type = withNoneId.categoryId

  val p = Product("sasa", true) //Error:(30, 18) could not find implicit value for evidence parameter of type com.novak.Program.CartId[Boolean]
}

此解决方案涉及一些代码并依赖于隐式,但可以完成您想要实现的目标。
请注意,此解决方案不是完全密封的,可能会被“破解”。你可以作弊并做类似的事情——
  val hack: Product[Boolean] = Product("a", true)(new CartId[Boolean])
  val b: Boolean =hack.categoryId

对于一些更高级的解决方案,包括
* Miles Sabin (@milessabin) 在 Scala 中通过 Curry-Howard 同构的 Unboxed union types
* Scalaz/运算符
http://eed3si9n.com/learning-scalaz/Coproducts.html

关于scala - 保证存在可选字段时的类型安全,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29487772/

10-12 02:08