Scala Typeclasses与泛型

KCh*_*oux 9 generics scala implicit typeclass implicit-conversion

我一直在使用Scala中的类型类模式,但是当我使用的类型是通用的时,我无法弄清楚如何实现隐式伴随对象.

例如,假设我已经为类型类定义了一个特征,它提供了将事物放入Boxes的函数.

case class Box[A](value: A)

trait Boxer[A] {
  def box(instance: A): Box[A]
  def unbox(box: Box[A]): A
}

implicit object IntBoxer extends Boxer[Int] {
  def box(instance: Int) = Box(instance)
  def unbox(box: Box[Int]) = box.value
}

def box[A : Boxer](value: A) = implicitly[Boxer[A]].box(value)
def unbox[A : Boxer](box: Box[A]) = implicitly[Boxer[A]].unbox(box)
Run Code Online (Sandbox Code Playgroud)

这按预期工作,允许我提供Boxer各种类型的实现.但是,当我想要采取的类型本身就是通用的时候,我知道如何做到这一点.假设我想能够使用我Boxer的任何一个Seq[A].objectScala中的s不能包含类型参数,所以我不知道该去哪里:

// Will not compile - object cannot have type arguments
implicit object SeqBoxer[A] extends Boxer[Seq[A]] { ... }

// Will not compile - 'A' is unrecognized
implicit object SeqBoxer extends Boxer[Seq[A]] { ... }

// Compiles but fails on execution, as this doesn't implement an implicit
// conversion for _specific_ instances of Seq
implicit object SeqBoxer extends Boxer[Seq[_]] {
  def box(instance: Seq[_]) = Box(instance)
  def unbox(box: Box[Seq[_]]) = box.value
}

// Will not compile - doesn't technically implement Boxer[Seq[_]]
implicit object SeqBoxer extends Boxer[Seq[_]] {
  def box[A](instance: Seq[A]) = Box(instance)
  def unbox[A](box: Box[Seq[A]]) = box.value
}

// Compiles, but won't resolve with 'implicitly[Boxer[Seq[Foo]]]'
// I had high hopes for this one, too :(
implicit def seqBoxer[A]() = new Boxer[Seq[A]] {
  def box(instance: Seq[A]) = Box(instance)
  def unbox(box: Box[Seq[A]]) = box.value
}
Run Code Online (Sandbox Code Playgroud)

有没有办法支持泛型类型的隐式转换,而不必为每个内部类型隐含一个单独的对象?

Mic*_*jac 8

实际上,你真的很亲密.您需要从中删除括号seqBoxer[A].否则,编译器将此视为隐式转换() => Boxer[Seq[A]],而不仅仅是可用的隐式转换Boxer[Seq[A]].为了更好地衡量,将隐式方法的返回类型显式化也是一个好主意.

implicit def seqBoxer[A]: Boxer[Seq[A]] = new Boxer[Seq[A]] {
  def box(instance: Seq[A]) = Box(instance)
  def unbox(box: Box[Seq[A]]) = box.value
}

scala> box(Seq(1, 2, 3))
res16: Box[Seq[Int]] = Box(List(1, 2, 3))
Run Code Online (Sandbox Code Playgroud)

您实际上可以使用相同的方法Boxer[A]为任何创建通用A,应该要求以相同的方式运行.

implicit def boxer[A]: Boxer[A] = new Boxer[A] {
    def box(instance: A): Box[A] = Box(instance)
    def unbox(box: Box[A]): A = box.value
}

scala> box("abc")
res19: Box[String] = Box(abc)

scala> box(List(1, 2, 3))
res20: Box[List[Int]] = Box(List(1, 2, 3))

scala> unbox(res20)
res22: List[Int] = List(1, 2, 3)

scala> box(false)
res23: Box[Boolean] = Box(false)
Run Code Online (Sandbox Code Playgroud)