我想定义一个由通用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。但这感觉很讨厌,我希望有更好的解决方案。
答案 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