Scala Typeclass具有多个参数错误

Scala Typeclass具有多个参数错误,scala,types,functional-programming,typeclass,Scala,Types,Functional Programming,Typeclass,我遇到了一个奇怪的错误,与这个多参数typeclass实现中的类型有关 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

我遇到了一个奇怪的错误,与这个多参数typeclass实现中的类型有关

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)
我错过了什么


更新


我发现问题在于
atom
xml
在被用作创建
s1
的值时是
case对象。如果我使用
newatom{}
newxml{}
则执行正常。但是,我想知道为什么
案例对象被认为是与它们所代表的类型不同的类型?

正如已经指出的,问题在于:

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.type,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

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

    如何将此应用于您的示例

    最简单的方法是向上投射原子:

    val s1 = show("some show", Atom: Atom, new Xml {})
    
    显示
    类型类
    不能是不变的。首先,使
    show
    T
    上协变,这样a
    show[Atom.type,B]
    也是
    show[Atom,B]

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

    什么是原子?请发布您的问题的详细信息。缺少部分代码,请立即修复。抱歉和感谢对于这个详细的回答,我非常感激。谢谢。
    val s1 = show("some show", Atom: Atom, new Xml {})
    
    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
    }
    
    // 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