Scala Typeclass 具有多个参数错误

标签 scala types functional-programming 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)

我收到的错误是

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);}
                                ^

我在这里缺少什么?


更新

我发现问题是 atomxml 在用作创建 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 的类型参数由传递给它的对象推断出来。 s1show[Atom.type, Xml],因此编译器正在寻找隐式 TypeClass[show[Atom.type, Xml], Atom。 Service#print 的类型,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不能是不变的。首先,使 showT 协变,以便 show[Atom.type, B] 也是 show[Atom, B ]:

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

然后,使 TypeClassCD 逆变,这样 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/

相关文章:

haskell - 是否可以通过修改这个简单的 reducer 来展示不同的评估策略?

Java8 实例化谓词

scala - 如何避免在循环中分配的 var

c - 带符号的补码算法

c# - Scala collection 的seq/par/view/force 是否可以被视为违反统一返回类型原则?

go - 保存类型以供以后反射

Clojure : function returns nil rather an evaluated expression

scala - 在 Scala 中,如何检查一个 Map 是否包含来自另一个 Map 的所有条目?

scala - 如何避免scala的case类默认toString函数被覆盖?

scala - 辛格尔顿斯卡拉 Actor ?