定义要在scala中按案例类扩展的特征

The*_*ect 7 scala traits self-type case-class

我有一些case类,它们的tupled伴随对象中定义了一个方法.从下面的代码对象中可以看出,它只是代码重复.

case class Book(id: Int, isbn: String, name: String)

object Book {
  def tupled = (Book.apply _).tupled // Duplication
}


case class Author(id: Int, name: String)

object Author {
  def tupled = (Author.apply _).tupled // Duplication
}
Run Code Online (Sandbox Code Playgroud)

从另一个问题(scala自我类型可以强制执行案例类类型),似乎我们不能强制将特征的自我类型作为案例类.

有没有办法定义一个Tupled可以应用如下的特征(比如说)?

// What would be value of ???
trait Tupled {
  self: ??? =>

  def tupled = (self.apply _).tupled
}

// Such that I can replace tupled definition with Trait
object Book extends Tupled {
}
Run Code Online (Sandbox Code Playgroud)

Tra*_*own 12

因为FunctionNScala中的类型之间没有关系,所以如果没有arity级别的样板,就不可能做到这一点 - 没有办法在apply不枚举所有可能数量的成员的情况下抽象同伴对象的方法.

你可以用一堆CompanionN[A, B, C, ...]特性手工完成这个,但这很烦人.Shapeless提供了更好的解决方案,允许您编写如下内容:

import shapeless.{ Generic, HList }, shapeless.ops.product.ToHList

class CaseClassCompanion[C] {
  def tupled[P <: Product, R <: HList](p: P)(implicit
    gen: Generic.Aux[C, R],
    toR: ToHList.Aux[P, R]
  ): C = gen.from(toR(p))
}
Run Code Online (Sandbox Code Playgroud)

然后:

case class Book(id: Int, isbn: String, name: String)
object Book extends CaseClassCompanion[Book]

case class Author(id: Int, name: String)
object Author extends CaseClassCompanion[Author]
Run Code Online (Sandbox Code Playgroud)

您可以这样使用:

scala> Book.tupled((0, "some ISBN", "some name"))
res0: Book = Book(0,some ISBN,some name)

scala> Author.tupled((0, "some name"))
res1: Author = Author(0,some name)
Run Code Online (Sandbox Code Playgroud)

您可能甚至不想要该CaseClassCompanion部分,因为可以构造一个将元组转换为案例类的通用方法(假设成员类型对齐):

class PartiallyAppliedProductToCc[C] {
  def apply[P <: Product, R <: HList](p: P)(implicit
    gen: Generic.Aux[C, R],
    toR: ToHList.Aux[P, R]
  ): C = gen.from(toR(p))
}

def productToCc[C]: PartiallyAppliedProductToCc[C] =
  new PartiallyAppliedProductToCc[C]
Run Code Online (Sandbox Code Playgroud)

然后:

scala> productToCc[Book]((0, "some ISBN", "some name"))
res2: Book = Book(0,some ISBN,some name)

scala> productToCc[Author]((0, "some name"))
res3: Author = Author(0,some name)
Run Code Online (Sandbox Code Playgroud)

这适用于最多包含22个成员的案例类(因为apply如果有超过22个参数,则无法将伴随对象上的方法扩展为函数).

  • 作为旁注,`fromTuple`可能是这个方法的一个更好的名称 - 你确实正在使用`apply`,但调用结果`tupled` make听起来好像转换是_to_元组而不是_from_. (2认同)