0

考虑这个示例,其中Listable旨在混合到案例类的伴随对象中。因此,为了调用Writer.grid,必须有一个A扩展的伴生对象Listable[A],并在其中implicit Writer[A]定义。(例如,将任意列表转换为ListableCSV 格式。)

trait Listable[A] {
    def list: List[A]
}

object Writer {
    def grid[A <: Listable[A]](listable: A)(implicit w: Writer[A]): String = {  
        listable.list.map(w.write(_).mkString(",")).mkString("\n")
    }
}

trait Writer[A] {
    def write(a: A): List[String]
}

这是一个天真的实现:

case class Test(id: Int, text: String)

object Test extends Listable[Test] {
    def list = List(Test(1, "test"))

    implicit val wrt = new Writer[Test] {
        def write(t: Test) = List(t.id.toString, t.text)
    }
}

这可以编译,但不能工作,因为listable: A真正指的是 object Test,而Ainw: Writer[A]指的是 case class Test,所以调用Writer.grid(Test)不符合类型边界。

Listable我可以通过放弃并要求implicit List[A]签名来解决这个问题grid

def grid[A](implicit w: Writer[A], list: List[A]): String = ...

但我更愿意:

  1. 不需要这样一个可能产生意外结果的隐式函数。
  2. 不要使用特殊类型来 wrap list,因为它也会在其他地方使用。
  3. grid将方法的定义保留Listable.

是否可以重新签名Writer.grid以使其工作?(或其他结构变化)

4

2 回答 2

1

你想要一些奇怪的东西。案例类 Test 显然具有类型 Test,但伴随对象 Test 与类型“Test”没有任何关系,它是一种类型“Test.type”。

我建议您制作 2​​ 个正确的类型类:Listable 和 Writer

  trait Listable[A] {
    def list: List[A]
  }

  object Writer {
    def grid[A : Listable : Writer](listable: A): String = {
      implicitly[Listable[A]].list.map(implicitly[Writer[A]].write(_).mkString(",")).mkString("\n")
    }
  }

  trait Writer[A] {
    def write(a: A): List[String]
  }

  case class Test(id: Int, text: String)

  object Test {
    implicit val listable: Listable[Test] = new Listable[Test] {
       def list: List[Test] = List(Test(1, "test"))
    }

    implicit val wrt: Writer[Test] = new Writer[Test] {
      def write(t: Test) = List(t.id.toString, t.text)
    }
  }

  Writer.grid(Test(123, "abc"))
于 2014-11-13T01:42:50.903 回答
1

基本上 Engene 指出了一个可行的解决方案,我只想说,您的情况真正奇怪的是,您不需要listable网格函数中的实例。从您的原始代码来看,这一事实并不那么明显,但是如果您从 Eugene 的代码中看到,它就很明显了,根本没有使用该参数。

所以我猜你想要做的是让伴生对象有一个用于特殊目的的特殊实例的实例,也许我错了。但是这个名字Listable[A]真的很混乱,如果你想描述伴生对象的一些特征,你可以命名为 sth like ListableMeta[A],这样更有意义。

trait ListableMeta[A] {
    def list: List[A]
}

object Writer {
    def grid[A : ListableMeta](implicit w: Writer[A]): String = {
        implicitly[ListableMeta[A]].list.map(w.write(_).mkString(",")).mkString("\n")
    }
}

trait Writer[A] {
    def write(a: A): List[String]
}

case class Test(id: Int, text: String)

trait InImplicit {
    implicit val injectThisToImplicits: this.type = this
}

object Test extends ListableMeta[Test] with Writer[Test] with InImplicit {
    def list = List(Test(1, "test"))

    def write(t: Test) = List(t.id.toString, t.text)
}

你需要做的基本上是Writer.grid[Test]而不是传递一个具体的实例,所以这应该工作。但这里明确的一点是在所有事物上都使用类型类,混合和匹配类型类和类型边界会导致混淆。

于 2014-11-13T03:02:34.823 回答