我希望编写一个Scala方法,它接受任意大小和类型的元组以及索引,并返回该索引处元组中的元素。我知道如何做所有事情,但保留类型。我还没有找到一种方法来使返回值成为元组项的动态类型。
这是我到目前为止的功能:
def subscript_get(tup: Product, index:Int): Any={
return tup.productElement(index)
}
例如,用法是:
subscript_get((0,1,2,3),0)
--> Int = 0
subscript_get((0,1,"asdf",3),2)
--> java.lang.String = asdf
我知道我可以将结果反馈给我正在寻找的东西,但这对我不起作用,因为我不能总是知道我应该选择什么类型。
这样的事情是否可能?谢谢!
答案 0 :(得分:11)
我不确定你想要一个使用宏的解决方案,但是对于记录(因为我之前已经准确地编写过这个方法),这里是如何用2.10中的宏系统实现的。
正如我在上面的评论中所指出的,这种方法要求index
为整数文字,并依赖于2.10中的"underspecified but intended" behavior。它还提出了关于文档的some tricky 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!"
)
}
}
}
然后:
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
所有静态类型都按预期输入,如果你给它一个超出范围(或不是文字)的索引,你会得到一个很好的编译时错误。
答案 1 :(得分:0)
你做不到。如果使用Product
,则元组中值的(编译时)类型将丢失。此外,方法无法根据您传入的值调整其返回类型(不完全正确,请参阅dependent method types,但对于Int
则为true。)
如果您不知道要转换的类型,可以使用模式匹配:
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")
}