2016-11-28 83 views
3

如果爲其超類型A定義了隱式,是否可以解析類型B的隱式參數?解析超類型的隱式參數

下面是一個例子:

我有一個可枚舉類型類:

trait Enumerable[A] { 

    def name(a: A): String 

    def list: List[A] 

    //... other methods 
} 

object Enumeration { 
    def name[A, T >: A](a: A)(implicit ev: Enumerable[T]) = ev.name(a) 

    def list[T](implicit ev: Enumerable[T]) = ev.list 

    // ... 
} 

然後我定義枚舉的一個實例:

sealed trait Season 

case object Winter extends Season 
case object Spring extends Season 
case object Summer extends Season 
case object Fall extends Season 

implicit val seasonEnumerable = new Enumerable[Season] { 
    override def list: List[Season] = List(Winter, Spring, Summer, Fall) 
} 

// working : 
Enumeration.name(Winter: Season) shouldBe "winter" 

// faling : 
Enumeration.name(Winter) shouldBe "winter" 

Enumeration.name(冬)是,如果失敗我不告訴scalac冬天是一個季節。我已經指定'name'方法簽名中的隱式參數是A的超類型,但它是不夠的...

有沒有更好的方法來做到這一點?

回答

3

Eduardo的回答解釋了爲什麼與[A, T >: A]版本不起作用。但有一個解決問題的簡單的解決方案比他給人的:不是引入T推斷的類型參數,介紹它由生存型:

def name[A](a: A)(implicit ev: Enumerable[T >: A] forSome { type T }) = ev.name(a) 

或者使用簡寫,

def name[A](a: A)(implicit ev: Enumerable[_ >: A]) = ev.name(a) 

然後編譯器只需要決定什麼時候尋找ev就是T

+0

確實!謝謝:) 你能否解釋一下def name [A](a:A)(implicit ev:Enumerable [_>:A])= ev.name(a) 和def name [A,T>: A](a:A)(隱式ev:Enumerable [T])= ev.name(a)? – Loic

+1

我已經擴大了答案。 –

3

無論何時您需要推斷依賴於類型的類型,這都是常見的不便之處。你的方法

def name[A, T >: A](a: A)(implicit ev: Enumerable[T]) 

呼籲Winter時,第一A會被推斷爲Winter.type然後TA,因爲它符合該約束,並有在這一點上沒有更多的約束。那麼編譯器當然不會找到Enumerable[Winter.type]的實例。

有與類型成員雖然一個簡單的解決方案:

trait AnyEnumerable { 

    type E 

    def name[A <: E](a: A): String 
    def list: List[E] 
} 

object Enumeration { 

    def name[A](a: A)(implicit ev: AnyEnumerable { type E >: A }) = ev.name(a) 
    def list[T](implicit ev: AnyEnumerable { type E = T }) = ev.list 
    // ... 
} 

// an implicit for `Season` 
implicit val seasonEnumerable: AnyEnumerable { type E = Season } = 
    new AnyEnumerable { 

    type E = Season 

    def name[A <: Season](a: A): String = a.toString 
    def list: List[Season] = List(Winter, Spring, Summer, Fall) 
    } 

// compiles! 
val zzz = Enumeration.name(Winter)