Как написать ограничения типов, которые соответствуют нескольким классам дел с разными сигнатурами - PullRequest
0 голосов
/ 22 января 2020

Предположим, у меня есть несколько классов дел, которые имеют одну общую черту:

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

Я хочу создать функцию, которая принимает любой из этих классов дел в качестве аргумента. Что-то вроде:

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, что является признаком и не принимает аргументов - система типов не может сделать вывод, что класс, который наследует свойство, может иметь аргументы. Мне нужен какой-то способ указать, что cc действительно принимает аргументы (см. Вопрос 1). Как мне выполнить sh это?

Оптимально, я хотел бы изменить черту MyCaseClasses, чтобы указать, что классы, которые ее расширяют, будут иметь любое количество дополнительных аргументов Any тип, но как это будет сделано, также неясно.

1 Ответ

1 голос
/ 22 января 2020

Как насчет этого:

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). Это может работать с использованием shapeless в scala2:

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)
...