2016-09-18 27 views
2

我得到相關類型的這種多參數的類型類實現一個奇怪的錯誤Scala的類型類有多個參數錯誤

trait Feedtype 
trait Atom extends Feedtype 
trait Rss2 extends Feedtype 
case object Atom extends Atom 
case object Rss2 extends Rss2 

trait Encoding 
trait Xml extends Encoding 
trait Json 
case object Json extends Json 
case object Xml extends Xml 

trait Content 
case class show[T <: Feedtype,E <: Encoding](str: String, tp: T, en: E) extends Content 

trait TypeClass[C,D,E] { 
    def show(c: C, d: D, e:E): String 
} 

trait Service{ 
    def print[T,C,D](t: T,c:C, d:D)(implicit s: TypeClass[T,C,D]): String 
} 

object Service extends Service{ 
    def print[T,C,D](t:T, c:C, d:D)(implicit s: TypeClass[T,C,D]): String = 
    s.show(t,c,d) 
} 

implicit val t1 = new TypeClass[show[Atom,Xml], Atom, Xml] { 
    def show(c: show[Atom,Xml], d:Atom, e:Xml) = c.str 
} 

implicit val t2 = new TypeClass[show[Rss2,Xml], Rss2, Xml] { 
    def show(c: show[Rss2,Xml], d:Rss2, e:Xml) = "hi there " + c.str 
} 

val s1 = show("some show", Atom, new Xml {}) 

Service.print(s1, s1.tp, s1.en) 

我得到的錯誤是

type mismatch; 
found : A$A10.this.typeclass[A$A10.this.show[A$A10.this.atom,A$A10.this.xml],A$A10.this.atom,A$A10.this.xml] 
required: A$A10.this.typeclass[A$A10.this.show[A$A10.this.atom.type,A$A10.this.xml.type],A$A10.this.atom.type,A$A10.this.xml.type] 
service.print(s1, s1.tp, s1.en)(t1);} 
           ^

我是什麼在這裏失蹤?


更新

我想通了,問題是,​​和xmlcase objects當它們被用作值創建s1。如果我使用new atom {}new xml {},那麼執行正常。但是,我想知道爲什麼case object被認爲是與它們所代表的類型不同的類型?

+0

什麼是'atom'?請發佈你的問題[MCVE]。 –

+0

錯過了代碼的一些部分,現在已經修復了。道歉和感謝 – kaychaks

回答

2

正如所指出的問題就出在這裏:

val s1 = show("some show", Atom, new Xml {}) 
       //   ^
       // The most specific type of this is Atom.type 

Service#print有通過傳遞給它的對象推斷出它的類型參數。 s1show[Atom.type, Xml],因此編譯器正在爲Service#print尋找隱含的TypeClass[show[Atom.type, Xml], Atom.type, Xml]。編譯器不會自動嘗試上溯造型的Atom.type(投對象)的Atom了幾個原因:

  1. 編譯器不知道show[Atom.type, Xml]也是show[Atom, Xml]show被聲明爲不變的,所以是TypeClass)。

  2. 暗含TypeClass[show[Atom,Xml], Atom, Xml]可用於通用Atom,但不適用於更具體的類型Atom.type


你舉的例子是非常複雜的,但並不需要註冊後才能重現該問題。考慮這個:

trait TypeClass[A] { 
    def show(a: A): String 
} 

trait Atom 
case object Atom extends Atom 

implicit val ar = new TypeClass[Atom] { 
    def show(a: Atom): String = "Atom" 
} 

object Service { 
    def print[A](a: A)(implicit tc: TypeClass[A]) = tc.show(a) 
} 

scala> Service.print(Atom) 
<console>:18: error: could not find implicit value for parameter tc: TypeClass[Atom.type] 
     Service.print(Atom) 
        ^

我們得到相同的編譯錯誤。這是因爲在上述結構中,就編譯器而言,TypeClass[Atom.type]TypeClass[Atom]並不相同,但它不一定是那種方式。我們可以製作TypeClass逆變型超過A,這意味着可以用TypeClass[Atom]代替TypeClass[Atom.type]。在這種情況下這樣做是有道理的,因爲在我的例子中TypeClass代表了一個類似打印機的類。如果你知道如何打印超類型,你可以打印一個子類型,但不一定是相反的。

trait TypeClass[-A] { 
    def show(a: A): String 
} 

這將允許隱式現在解決。如果你想保持不變TypeClass,我們可以向上轉換的Atom自己:

scala> Service.print(Atom: Atom) 
res4: String = Atom 

如何將其應用到你的榜樣?

最簡單的方法將是向上轉換Atom

val s1 = show("some show", Atom: Atom, new Xml {}) 

或者showTypeClass不可能是不變的。首先,讓show協變了T使得show[Atom.type, B]也是show[Atom, B]

case class show[+T <: Feedtype, E <: Encoding](str: String, tp: T, en: E) extends Content 

然後,使TypeClass逆變過CD,從而使TypeClass[show[Atom,Xml], Atom, Xml]也可以被認爲是TypeClass[show[Atom.type, Xml], Atom.type,Xml],這將使隱t1被拾起。

trait TypeClass[-C, -D, E] { 
    def show(c: C, d: D, e:E): String 
} 

// cutting out putting it all together to save space 

scala> val s1 = show("some show", Atom, new Xml {}) 
s1: show[Atom.type,Xml] = show(some show,Atom,[email protected]) 

scala> Service.print(s1, s1.tp, s1.en) 
res1: String = some show 
+0

我無法多說我多麼感激這個詳細的答案。非常感謝。 – kaychaks