построить класс case из набора параметров

Дано:

case class Thing(a:Int, b:String, c:Double)

val v = Vector(1, "str", 7.3)

Я хочу что-то, что будет волшебным образом создавать:

Thing(1, "str", 7.3)

Существует ли такая вещь (для Вещей произвольного размера)?


person Eve Freeman    schedule 09.02.2013    source источник
comment
Интересно, справится ли с этим новое отражение 2.10. Вы ограничены 2.9.x?   -  person huynhjl    schedule 09.02.2013


Ответы (3)


Я впервые погружаюсь в экспериментальные объекты отражения 2.10. Так что в основном следуя этой схеме http://docs.scala-lang.org/overviews/reflection/overview.html, я придумал это:

import scala.reflect.runtime.{universe=>ru}

case class Thing(a: Int, b: String, c: Double)

object Test {
  def main(args: Array[String]) {
    val v = Vector(1, "str", 7.3)
    val thing: Thing = Ref.runtimeCtor[Thing](v)
    println(thing) // prints: Thing(1,str,7.3)
  }
}

object Ref {
  def runtimeCtor[T: ru.TypeTag](args: Seq[Any]): T = {
    val typeTag = ru.typeTag[T]
    val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)

    val classSymbol = typeTag.tpe.typeSymbol.asClass
    val classMirror = runtimeMirror.reflectClass(classSymbol)

    val constructorSymbol = typeTag.tpe.declaration(ru.nme.CONSTRUCTOR).asMethod
    val constructorMirrror = classMirror.reflectConstructor(constructorSymbol)
    constructorMirrror(args: _*).asInstanceOf[T]
  }
}

Обратите внимание, что когда у меня был класс case внутри основного метода, он не компилировался. Я не знаю, могут ли теги типов создаваться только для классов, не являющихся внутренними.

person huynhjl    schedule 09.02.2013
comment
Спасибо, поиграю с этим. - person Eve Freeman; 09.02.2013

Я не знаю, возможно ли получить рабочее решение с ошибкой времени компиляции, но это мое решение с использованием сопоставления:

case class Thing(a: Int, b: String, c: Double)
def printThing(t: Thing) {
  println(t.toString)
}

implicit def vectToThing(v: Vector[Any]) = v match {
  case (Vector(a: Int, b: String, c: Double)) => new Thing(a, b, c)
}

val v = Vector(1, "str", 7.3) // this is of type Vector[Any]
printThing(v) // prints Thing(1,str,7.3)
printThing(Vector(2.0, 1.0)) // this is actually a MatchError

Есть ли реальная цель этого преобразования «Вещь» или вы бы предпочли использовать Tuple3[Int,String,Double] вместо Vector[Any]?

person michael_s    schedule 09.02.2013
comment
Мне нужно, чтобы это не было привязано к длине коллекции. Это не обязательно должен быть вектор, но он должен быть произвольной длины. - person Eve Freeman; 09.02.2013
comment
ОК, верно, но в конце концов вам придется создать какой-то объект case, верно? И этот объект case будет принимать определенное количество параметров определенных типов. Не могли бы вы привести примеры того, как вы бы использовали свою библиотеку? - person michael_s; 09.02.2013

Из вашего вопроса неясно, для чего вы будете его использовать. То, что вы называете вещью, на самом деле может быть HList или KList. HList означает Гетерогенные списки, которые представляют собой кортежи произвольной длины.

Я не уверен, насколько сложно было бы добавить метод unnapply или unapplySeq, чтобы он вел себя как класс case.

У меня мало опыта работы с ними, но хорошее объяснение можно найти здесь: http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/

Если это не то, что вам нужно, было бы неплохо сообщить нам, чего вы хотите достичь.

person EECOLOR    schedule 09.02.2013
comment
Это для библиотеки, которая будет принимать параметр типа класса case для преобразования вещей в класс case. Надеялся на универсальное решение. - person Eve Freeman; 09.02.2013
comment
Не могли бы вы написать, как бы вы хотели вызвать этот метод? Это что-то вроде library.construct[CaseClassType]("one" :: 1 :: true :: Nil)? - person EECOLOR; 09.02.2013
comment
Если аргументы конструктора приходят не как Seq[Any], а как что-то похожее на кортеж, то вам не нужно писать свой собственный HList. Вы можете использовать github.com/milessabin/shapeless. - person huynhjl; 09.02.2013