2015-04-07 42 views
3

比方說,我有一個下列情況下類:類型安全時,可選字段是保證是目前

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類型系統來聲明這個沒有這些字段重複的特定情況?

+0

首先....有什麼問題'Option [Long]'?但是如果你想要的話,你可以定義一個隱式TypeClass'ProductWithAssuredCategoryId'。用這個提供額外的方法。 –

+0

如果你還不瞭解TypeClass,你可以要求一個例子。 –

+0

DAO的調用者希望產品具有categoryId,如果它是None,他們沒有任何意義。但它也聞到,如果他們只是'product.categoryId.get' –

回答

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 

對於一些更多 - 先進的解決方案,其中包括 *萬里薩賓(@milessabin)的斯卡拉無盒裝聯盟類型經咖喱霍華德同構 * Scalaz /運營商 http://eed3si9n.com/learning-scalaz/Coproducts.html

2

不知道有多少,這將擴大對你的具體情況,但我想到的一個解決方案是使用-kinded更高的泛型類型在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]] = ??? 
} 
+0

我該如何獲得List [Product]?你能舉個例子嗎? –

+0

@ Oleksandr.Bezhan對不起,我在代碼片段中有錯誤。案例類應該是「產品」,而不是「類別」。現在有意義嗎? –

+0

@Ionut是的,現在很清楚。它使我所要求的,但似乎很複雜,並不像其他解決方案一樣可讀。我寧願選擇複雜性和複雜性的可讀性。但如果沒有人提供更好的解決方案,我會將其標記爲正確的答案。謝謝。 –