I am hoping to write a Scala method which takes in a tuple of any size and type along with an index, and returns the element in the tuple at that index. I know how to do everything but preserve the type. I haven't yet figured out a way to make the return value be of the dynamic type of the tuple item.
Here is the function I have so far:
def subscript_get(tup: Product, index:Int): Any={
return tup.productElement(index)
}
The usage for example would be:
subscript_get((0,1,2,3),0)
--> Int = 0
subscript_get((0,1,"asdf",3),2)
--> java.lang.String = asdf
I know that I can cast the result back afterwards to what I am looking for, but this doesn't work for me because I can't always know what type I should cast to.
Is something like this even possible ? Thanks!
I'm not sure you want a solution that uses macros, but for the record (and since I've written precisely this method before), here's how you can implement this with the macro system in 2.10.
As I note in a comment above, this approach requires index
to be an integer literal, and relies on "underspecified but intended" behavior in 2.10. It also raises some tricky questions about documentation.
import scala.language.experimental.macros
import scala.reflect.macros.Context
object ProductIndexer {
def at[T <: Product](t: T)(index: Int) = macro at_impl[T]
def at_impl[T <: Product: c.WeakTypeTag](c: Context)
(t: c.Expr[T])(index: c.Expr[Int]) = {
import c.universe._
index.tree match {
case Literal(Constant(n: Int)) if
n >= 0 &&
weakTypeOf[T].members.exists {
case m: MethodSymbol => m.name.decoded == "_" + (n + 1).toString
case _ => false
} => c.Expr[Any](Select(t.tree, newTermName("_" + (n + 1).toString)))
case Literal(Constant(_: Int)) => c.abort(
c.enclosingPosition,
"There is no element at the specified index!"
)
case _ => c.abort(
c.enclosingPosition,
"You must provide an integer literal!"
)
}
}
}
And then:
scala> import ProductIndexer._
import ProductIndexer._
scala> val triple = (1, 'a, "a")
triple: (Int, Symbol, String) = (1,'a,a)
scala> at(triple)(0)
res0: Int = 1
scala> at(triple)(1)
res1: Symbol = 'a
scala> at(triple)(2)
res2: String = a
All statically typed as expected, and if you give it an index that's out of range (or not a literal), you get a nice compile-time error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With