Find arity type product without copy

I want to define a function that is parameterized by the generic type of the product, but which can solve the Product problem. Here is an example fragment. I would like to do an arity check when f (...) is called, and not when f (...) () is called. How can i do this?

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

(This is a rather useless function, just for demonstration. The important points are: (1) it is parameterized by the Product type; (2) the function makes sense only if the arity of the product corresponds to a certain value, which when I call the function, (3) to me It’s expensive or impossible to get an instance of the Product when I call the function. My actual use case is a utility class for writing SQL statements based on spark RDD.)

If necessary, I could write a whole family of functions, one for each Tuple size. But this is unpleasant, and I hope for a better solution.

+4
source share
3 answers

Slightly better than writing various methods, you can find using type classes:

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

Of course, you will need to write objects Arityfor all possible tuple sizes. You can automate this by generating code or (if you're cocky and patient) using macros.

+2
source

You can try to determine nameshow Productthe same subtype is:

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 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 type parameter bounds [T <: Product]
              f(("one"), () => ("1", "2"))()
              ^

, , - . List Tuple : ( )?.

, HList Product - ;)

0

Here is a reflection solution that will work for products 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
}

Minimal example:

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

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

Source: https://habr.com/ru/post/1530082/


All Articles