Scala中是否有任何方法可以获得动态数量的命名/类型参数?

时间:2015-07-04 21:56:26

标签: scala

我正在研究一些Scala代码,其中用户将拥有可变数量的命名变量,所有类型A都相同:

val a1: A = getA1()
val a2: A = getA2()
val a3: A = getA3()
// ...

我想定义一个名为Processor的类,它将这些参数作为参数,将每个参数转换为新类型B,并公开一个名为process的方法,为用户提供访问这些新的转换变量。我到目前为止提出的最好的API是:

class Processor(a1: A, a2: A, a3: A) {
  private val b1 = toB(a1)
  private val b2 = toB(a2)
  private val b3 = toB(a3)

  def process(f: (B, B, B) => C): C = {
    f(b1, b2, b3)
  }
}

// Usage:

val a1: A = getA1()
val a2: A = getA2()
val a3: A = getA3()

val p = new Processor(a1, a2, a3)
p.process((b1, b2, b3) => doSomething(b1, b2, b3))

此API的问题在于它仅适用于三个参数,而用户可以拥有1到20个参数。我可以定义Processor1Processor2Processor3等等,每个类都采用不同数量的参数,但这将是一个重复且丑陋的API。另一种选择是在A*构造函数和Processor方法中将process作为参数:

class Processor(a: A*) {
  private val b = a.map(toB)

  def process(f: (Seq[B]) => C): C = {
    f(b)
  }
} 

// Usage:

val a1: A = getA1()
val a2: A = getA2()
val a3: A = getA3()

val p = new Processor(Vector(a1, a2, a3))
p.process((b) => doSomething(b(0), b(1), b(2)))  

此API适用于任意数量的参数,但它不再具有类型安全性或命名参数,因此如果用户意外查找不存在的索引(例如b(3)),代码可能会在运行时爆炸)。换句话说,编译器不再验证传递给Processor构造函数的参数数量与process方法中使用的数量相匹配。

Scala中是否有任何方法可以获得动态数量的命名/类型化参数?也许使用宏?

1 个答案:

答案 0 :(得分:3)

shapeless的帮助下,您可以定义Processor,如:

class Processor[T](t: T)
 (implicit
   mapper: ops.tuple.Mapper.Aux[T, ToB.type]
 ) {
  private val bs = mapper(t)

  def process[C](f: mapper.Out => C): C = {
    f(bs)
  }
}

shapeless._之前已导入。)

可以像

一样使用
val p4 = new Processor(a1, a2, a3, a4)
p4.process{case (b1, b2, b3, b4) => ??? }

此处,Processor使用类型为A的{​​{1}}元组进行实例化时,需要隐式T,以便映射shapeless.ops.tuple.Mapper (包含在toB中,见下文)此元组。此隐式的类型成员ToB是由Out s组成的“输出”元组。它用于B的签名。

process定义为

ToB

并允许使用object ToB extends Poly1 { implicit def apply = at[A](a => toB(a)) } 在元组上映射toB