假设我有以下案例类:
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_ !,但它要求您创建两个案例类和一个重复所有字段的特征,这些字体也有气味。
我的问题:如何在没有这些字段重复的情况下使用Scala类型系统声明此特定情况?
答案 0 :(得分:2)
不确定这会针对您的具体情况进行多大扩展,但我想到的一个解决方案是使用更高级别的通用类型对Option
类型进行参数化:
object Example {
import scala.language.higherKinds
type Id[A] = A
case class Product[C[_]](name: String, category: C[Long])
def productsWithoutCategories: List[Product[Option]] = ???
def productsWithCategories: List[Product[Id]] = ???
}
答案 1 :(得分:1)
一种方法是使用类型类 -
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]
}
此解决方案涉及一些代码并依赖于implicits,但会执行您尝试实现的内容。 请注意,此解决方案并未完全密封,并且可以被黑客入侵。你可以欺骗并做一些像 -
val hack: Product[Boolean] = Product("a", true)(new CartId[Boolean])
val b: Boolean =hack.categoryId
对于一些更高级的解决方案,包括 * Miles Sabin(@milessabin)通过Curry-Howard同构在Scala中的Unboxed联合类型 * Scalaz /运营商 http://eed3si9n.com/learning-scalaz/Coproducts.html