简体   繁体   English

Scala中保存动态类型的元组的通用getter方法?

[英]Generic getter method for tuples in Scala which preserves dynamic type?

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. 我希望编写一个Scala方法,它接受任意大小和类型的元组以及索引,并返回该索引处元组中的元素。 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. 我不确定你想要一个使用宏的解决方案,但是对于记录(因为我之前已经准确地编写了这个方法),这里是如何用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. 正如我在上面的评论中所指出的,这种方法要求index是一个整数文字,并依赖于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. 所有都按预期静态输入,如果你给它一个超出范围(或不是文字)的索引,你会得到一个很好的编译时错误。

You cannot do that. 你不能这样做。 If you use Product , the (compile-time) type of the values in the tuples is lost. 如果使用Product ,则元组中值的(编译时)类型将丢失。 Further, a method cannot adapt its return type based on an value you pass in (not entirely true, see dependent method types , but true for an Int ). 此外,方法无法根据您传入的值调整其返回类型(不完全正确,请参阅依赖方法类型 ,但对于Int ,则为true)。

If you do not know what type to cast to, you could 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")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM