如何编写匹配具有不同签名的多个案例 类 的类型约束

How to write type constraints that match several case classes with different signatures

假设我有一些案例 class 有一个共同特征:

trait MyCaseClasses {
  def generic: String
}
case class C1(l: List[Int], generic: String) extends MyCaseClasses
case class C2(s: String, generic: String) extends MyCaseClasses
case class C3(a: Int, b: Int, generic: String) extends MyCaseClasses

我想做的是创建一个函数,将这些情况 class 中的任何一个作为参数。类似于:

def caseClassCurry[T <: MyCaseClasses](generic: String, cc: T): (args) => T = {
  (args) => cc(args, generic)
}

当然,这不会编译。这里有两个问题不知道怎么解决:

  1. args 很难指定。它必须表示任何类型的可变数量的参数。你会怎么做?

  2. 当我写出这个版本时,我得到 cc.type does not take parameters。我假设这是因为 cc 只继承了 MyCaseClasses 的特性,这是一个特征并且不接受参数 - 类型系统无法推断出 class 继承了特质可能有争论。我需要某种方式来表明 cc 实际上确实接受了参数(请参阅问题 1)。我将如何完成此操作?

最理想的是,我想更改特征 MyCaseClasses 以指示扩展它的 classes 将具有任意数量的 Any 类型的附加参数,但是那将如何是否大功告成也不清楚。

这个怎么样:

scala> trait F[T]{ def generic: String }
// defined trait F
scala> case class C1(l: List[Int], generic: String) extends F[List[Int]]
// defined case class C1
scala> case class C2(s: String, generic: String) extends F[String]
// defined case class C2

scala> def caseClassCurry[T](generic: String, cc: (T, String) => F[T]): T => F[T] = cc(_, generic)
def caseClassCurry[T](generic: String, cc: (T, String) => F[T]): T => F[T]

scala> caseClassCurry("c1", C1.apply)(List(1, 2, 3))
val res2: F[List[Int]] = C1(List(1, 2, 3),c1)

** 编辑 ** 很抱歉在上述解决方案中遗漏了 C3 案例,我找到了另一个解决方案,但仅适用于 dotty (即 scala3.0)。它可以通过在 scala2 中使用 shapeless 来工作:

scala> trait F { def generic: String }
// defined trait F

scala> case class C1(generic: String, list: List[Int]) extends F
// defined case class C1

scala> case class C2(generic: String, str: String) extends F
// defined case class C2

scala> case class C3(generic: String, a: Int, b: Long) extends F
// defined case class C3

scala> def curry[T <: Tuple, C <: F](generic: String, cc: String *: T => C): T => C = t => cc(generic *: t)
def curry[T <: Tuple, C <: F](generic: String, cc: String *: T => C): T => C

scala> curry("hello", C1.apply.tupled)(Tuple1(List()))
val res0: C1 = C1(hello,List())

scala> curry("hello", C2.apply.tupled)(Tuple1("world"))
val res1: C2 = C2(hello,world)

scala> curry("hello", C3.apply.tupled)((0, 1L))
val res2: C3 = C3(hello,0,1)