2017-01-16 92 views
3

我想讓編譯器根據已知的其他2個類型參數推斷某種類型。這裏的例子:使用implicits來推斷未知類型

trait ReturnCount 
trait ReturnsMany extends ReturnCount 
trait ReturnsOne extends ReturnCount 

class Query[R <: ReturnCount]{ 
    def join[R2 <: ReturnCount,R3 <: ReturnCount](q: Query[R2]): Query[R3] 
} 

正如你可以在這裏看到我希望能夠加入兩個讀查詢(組成了這是無關緊要的細節)。生成的新查詢必須是ReturnsOneReturnsMany結果的查詢。解決的規則也非常簡單:只有在查詢ReturnsOne時,加入的查詢在所有其他情況下也是ReturnsOneReturnsMany

所以:

val q1 = new Query[ReturnsOne] 
val q2 = new Query[ReturnsMany] 
val q3 = q1 join q2 //I don't want to have 
        //to specify R3 because compiler should do it for me... 

我怎麼能希望實現這一目標?

+0

如果''''R'''的類型爲'ReturnsOne''',那麼我需要'''R2'''來知道查詢返回的結果數量。如果R的類型爲'''ReturnsMany''',那麼你是對的,它不會產生任何影響,因爲它將是''''ManyMany'''無論如何 – shayan

+0

爲什麼由'R2'確定的結果數量和不是'R'?這裏'R'的一般用法是什麼? –

+0

@YuvalItzchakov它由R3確定,R3需要R和R2來決定。我正在尋找這個隱含的參數,它使用R和R2來解析R3,使其更加具體 – shayan

回答

3

這就是你將如何做這種事情的情況下,這3種特徵是密封的。如果他們願意擴展,它可能會變得更加複雜。

歸結爲以下幾點。您有Fancy特徵,其中AB兩種輸入類型,以及一種輸出類型Out。您可以定義隱式實例,以便在AB均爲ReturnsOne,Out將爲ReturnsOne的情況下。在所有其他情況下,您將回退到默認情況下的優先級較低(因爲否則您將得到多義性錯誤),其中Out總是ReturnsMany

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

sealed trait ReturnCount 
sealed trait ReturnsMany extends ReturnCount 
sealed trait ReturnsOne extends ReturnCount 

sealed trait Fancy[A, B] { 
    type Out <: ReturnCount 
} 

object Fancy extends LowerPriority { 
    type Aux[A,B,Out1 <: ReturnCount] = Fancy[A,B] { type Out = Out1 } 

    implicit def returnsOne: Fancy.Aux[ReturnsOne,ReturnsOne,ReturnsOne] = 
    new Fancy[ReturnsOne,ReturnsOne] { type Out = ReturnsOne } 
} 

trait LowerPriority { 
    implicit def returnsMany[A,B]: Fancy.Aux[A,B,ReturnsMany] = 
    new Fancy[A,B] { type Out = ReturnsMany } 
} 

class Query[R <: ReturnCount] { 
    def join[R2 <: ReturnCount](q: Query[R2])(implicit fancy: Fancy[R,R2]): Query[fancy.Out] = ??? 
} 


// Exiting paste mode, now interpreting. 

defined trait ReturnCount 
defined trait ReturnsMany 
defined trait ReturnsOne 
defined trait Fancy 
defined object Fancy 
defined trait LowerPriority 
defined class Query 

scala> :type new Query[ReturnsOne].join(new Query[ReturnsOne]) 
Query[ReturnsOne] 

scala> :type new Query[ReturnsOne].join(new Query[ReturnsMany]) 
Query[ReturnsMany] 

scala> :type new Query[ReturnsMany].join(new Query[ReturnsMany]) 
Query[ReturnsMany] 

scala> :type new Query[ReturnsMany].join(new Query[ReturnsOne]) 
Query[ReturnsMany] 

在真實的場景中你Fancy可能還需要一個join方法,其中將包含實際的實現,你Query#join方法委託:

class Query[R <: ReturnCount] { 
    def join[R2 <: ReturnCount](q: Query[R2])(implicit fancy: Fancy[R,R2]): Query[fancy.Out] = 
    fancy.join(this, q) 
} 

This blog可能是一個很好的起點,以瞭解更多關於這樣的模式。

+0

漂亮。謝謝 – shayan