A generic getter method for tuples in Scala that preserves a dynamic type?

I hope to write a Scala method that takes a tuple of any size and type along with the index and returns the element in the tuple at that index. I know how to do everything except save type. I have not figured out a way to make the return value of the dynamic type of a tuple element.

Here is the function that I have so far:

def subscript_get(tup: Product, index:Int): Any={ return tup.productElement(index) } 

For example, usage 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 return the result back to what I am looking for, but this does not work for me, because I can not always know what type I should be attached to.

Is something like this possible? Thanks!

+6
source share
2 answers

I'm not sure if you want a solution that uses macros, but for writing (and since I wrote this particular method before), here is how you can implement this with the macro system in 2.10.

As I noted in the comment above, this approach requires index be a whole literal and relies on "underspecified, but supposed" behavior in 2.10. He also raises some difficult documentation questions .

 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 are statically typed as expected, and if you give it an index out of range (or not a literal), you will get a good compile-time error.

+11
source

You cannot do this. If you use Product , the type (compilation time) of values ​​in tuples is lost. In addition, the method cannot adapt its return type based on the value you pass (not quite true, see dependent method types , but true for Int ).

If you don’t know which type to use, you can use pattern matching:

 subscript_get(..., 1) match { case v: Int => // do something with Int case v: String => // do something with String // snip case _ => sys.error("don't know how to handle this") } 
0
source

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


All Articles