我收到一个与此多参数类型类实现的类型相关的奇怪错误
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);}
^
我在这里缺少什么?
更新
我发现问题是 atom
和 xml
在用作创建 s1 的值时是
。如果我使用 case 对象
newatom {}
或 new xml{}
那么执行会正常进行。但是,我想知道为什么 case 对象
被视为与其代表的类型不同的类型?
最佳答案
正如已经指出的,问题出在这里:
val s1 = show("some show", Atom, new Xml {})
// ^
// The most specific type of this is Atom.type
Service#print
的类型参数由传递给它的对象推断出来。 s1
是 show[Atom.type, Xml]
,因此编译器正在寻找隐式 TypeClass[show[Atom.type, Xml], Atom。
。编译器不会自动尝试将 Service#print
的类型,Xml]Atom.type
(强制转换对象)向上转换为 Atom
,原因如下:
编译器不知道
show[Atom.type, Xml]
也是show[Atom, Xml]
(show
被声明为不变式,TypeClass
也是如此。有一个隐式
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 {})
或者,show
和TypeClass
不能是不变的。首先,使 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
对 C
和 D
逆变,这样 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,$anon$1@76a4d6c)
scala> Service.print(s1, s1.tp, s1.en)
res1: String = some show
关于Scala Typeclass 具有多个参数错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39559806/