找到产品类型的arity,没有实例

时间:2014-03-04 19:09:03

标签: scala

我想定义一个由通用Product类型参数化的函数,但它可以解决Product的arity问题。这是一个示例代码段。我想在调用f(...)时进行arity检查,而不是在调用f(...)()时进行。我怎么能这样做?

def f[T<:Product](names:Seq[String], values:()=>T) = {
  () => {
    val x = values()
    if (x.productArity != names.size) scala.sys.error("Size mismatch")
    names.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2)
  }
}

(这是一个非常无用的功能,仅用于演示。重要的一点是(1)它是由产品类型参数化的,(2)只有当产品的arity与某个值相匹配时,该功能才有意义。当我调用函数时已知,(3)当我调用函数时,我很难/不可能获得Product的实例。我的实际用例是基于spark RDD写出SQL语句的实用程序类。)

如果有必要,我可以写出一整套函数,每个函数对应一个Tuple。但这感觉很讨厌,我希望有更好的解决方案。

3 个答案:

答案 0 :(得分:3)

使用类型类可以找到比编写不同方法更好的东西:

case class Arity[P]( get: Int )

object Arity {
  def apply[P]( implicit arity: Arity[P] ) = arity
  implicit def tuple2[A,B] = Arity[(A,B)]( 2 )
  implicit def tuple3[A,B,C] = Arity[(A,B,C)]( 3 )
  //...
} 

def f[T<:Product:Arity](names:Seq[String], values:()=>T) = {
  () => {
    val x = values()
    if ( Arity[T].get != names.size) scala.sys.error("Size mismatch")
    names.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2)
  }
}

当然,您需要为所有可能的元组大小写下Arity个对象。您可以使用代码生成或(如果您大胆而耐心)使用宏来自动执行此操作。

答案 1 :(得分:0)

您可以尝试将names定义为同一子类型的Product

def f[T<:Product](names: T, values:()=>T) = {
  () => {
    val x = values()
    names.productIterator.toList.zip(x.productIterator.map(_.toString).toSeq).map(kv => kv._1+"="+kv._2)
  }
}

scala> f(("one", "two"), () => ("1", "2"))()
res15: List[java.lang.String] = List(one=1, two=2)

scala> f(("one", "two"), () => ("1"))()
<console>:9: error: inferred type arguments [java.io.Serializable] do not conform to method f's type parameter bounds [T <: Product]
              f(("one", "two"), () => ("1"))()
              ^

scala> f(("one"), () => ("1", "2"))()
<console>:9: error: inferred type arguments [java.io.Serializable] do not conform to method f's type parameter bounds [T <: Product]
              f(("one"), () => ("1", "2"))()
              ^

当然,如果您在某些集合中使用它们,则传递名称会很不方便。您可以使用以下方法之一将List转换为Tuple,例如:Is there way to create tuple from list(without codegeneration)?

使用HList代替Product并不是一个坏主意;)

答案 2 :(得分:0)

以下是使用反射的解决方案,该解决方案适用于产品1-22:

import scala.reflect.runtime.universe._

def tupleArity(typ: Type): Int = {
    for(i <- 2 to 22) {
      if (typ.member(stringToTermName("_" + i)) == NoSymbol) return i - 1
    }
    22
}

最小例子:

def f[T <: Product](implicit tag: TypeTag[T]): Int = tupleArity(typeOf[T])

scala> f[Tuple2[_, _]]
res18: Int = 2