我正在研究一些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个参数。我可以定义Processor1
,Processor2
,Processor3
等等,每个类都采用不同数量的参数,但这将是一个重复且丑陋的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中是否有任何方法可以获得动态数量的命名/类型化参数?也许使用宏?
答案 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
。