是否可以将magnet pattern与varargs一起使用:
object Values {
implicit def fromInt (x : Int ) = Values()
implicit def fromInts(xs: Int*) = Values()
}
case class Values()
object Foo {
def bar(values: Values) {}
}
Foo.bar(0)
Foo.bar(1,2,3) // ! "error: too many arguments for method bar: (values: Values)Unit"
答案 0 :(得分:2)
正如gourlaysama已经提到的那样,将varargs变成单个Product
就可以解决这个问题,从句法上讲:
implicit def fromInts(t: Product) = Values()
这允许以下调用编译正常:
Foo.bar(1,2,3)
这是因为编译器自动将3个参数提升为Tuple3[Int, Int, Int]
。这将适用于任何数量的参数,直到22的arity。现在的问题是如何使其类型安全。因为它是Product.productIterator
是在方法体内返回我们的参数列表的唯一方法,但它返回Iterator[Any]
。我们无法保证仅使用Int
来调用该方法。这应该不足为奇,因为我们实际上从未在签名中提到我们只想要Int
s。
好的,所以无约束Product
和vararg列表之间的关键区别在于,在后一种情况下,每个元素的类型相同。我们可以使用类型类对此进行编码:
abstract sealed class IsVarArgsOf[P, E]
object IsVarArgsOf {
implicit def Tuple2[E]: IsVarArgsOf[(E, E), E] = null
implicit def Tuple3[E]: IsVarArgsOf[(E, E, E), E] = null
implicit def Tuple4[E]: IsVarArgsOf[(E, E, E, E), E] = null
implicit def Tuple5[E]: IsVarArgsOf[(E, E, E, E, E), E] = null
implicit def Tuple6[E]: IsVarArgsOf[(E, E, E, E, E), E] = null
// ... and so on... yes this is verbose, but can be done once for all
}
implicit class RichProduct[P]( val product: P ) {
def args[E]( implicit evidence: P IsVarArgsOf E ): Iterator[E] = {
// NOTE: by construction, those casts are safe and cannot fail
product.asInstanceOf[Product].productIterator.asInstanceOf[Iterator[E]]
}
}
case class Values( xs: Seq[Int] )
object Values {
implicit def fromInt( x : Int ) = Values( Seq( x ) )
implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int ) = Values( xs.args.toSeq )
}
object Foo {
def bar(values: Values) {}
}
Foo.bar(0)
Foo.bar(1,2,3)
我们更改了方法签名表单
implicit def fromInts(t: Product)
为:
implicit def fromInts[P]( xs: P )( implicit evidence: P IsVarArgsOf Int )
在方法体内,我们使用新方法args
来获取我们的arg列表。
请注意,如果我们尝试使用不是bar
元组的元组调用Int
,我们将收到编译错误,这会让我们恢复类型安全性。
UPDATE :正如0__所指出的,我的上述解决方案在数字扩展方面效果不佳。换句话说,以下内容无法编译,但如果bar
只是简单地获取3 Int
个参数,那么它将起作用:
Foo.bar(1:Short,2:Short,3:Short)
Foo.bar(1:Short,2:Byte,3:Int)
要解决此问题,我们需要做的就是修改IsVarArgsOf
以便所有隐含允许
元组元素可以转换为普通类型,而不是全部属于同一类型:
abstract sealed class IsVarArgsOf[P, E]
object IsVarArgsOf {
implicit def Tuple2[E,X1<%E,X2<%E]: IsVarArgsOf[(X1, X2), E] = null
implicit def Tuple3[E,X1<%E,X2<%E,X3<%E]: IsVarArgsOf[(X1, X2, X3), E] = null
implicit def Tuple4[E,X1<%E,X2<%E,X3<%E,X4<%E]: IsVarArgsOf[(X1, X2, X3, X4), E] = null
// ... and so on ...
}
好吧,实际上我说谎了,我们还没有完成。因为我们现在接受不同类型的元素(只要它们可以转换为公共类型,我们不能只将它们转换为期望的类型(这会导致运行时转换错误),而是我们必须应用隐式转换。我们可以像这样重做:
abstract sealed class IsVarArgsOf[P, E] {
def args( p: P ): Iterator[E]
}; object IsVarArgsOf {
implicit def Tuple2[E,X1<%E,X2<%E] = new IsVarArgsOf[(X1, X2), E]{
def args( p: (X1, X2) ) = Iterator[E](p._1, p._2)
}
implicit def Tuple3[E,X1<%E,X2<%E,X3<%E] = new IsVarArgsOf[(X1, X2, X3), E]{
def args( p: (X1, X2, X3) ) = Iterator[E](p._1, p._2, p._3)
}
implicit def Tuple4[E,X1<%E,X2<%E,X3<%E,X4<%E] = new IsVarArgsOf[(X1, X2, X3, X4), E]{
def args( p: (X1, X2, X3, X4) ) = Iterator[E](p._1, p._2, p._3, p._4)
}
// ... and so on ...
}
implicit class RichProduct[P]( val product: P ) {
def args[E]( implicit isVarArg: P IsVarArgsOf E ): Iterator[E] = {
isVarArg.args( product )
}
}
这解决了数字扩展的问题,在混合不相关的类型时我们仍然得到编译:
scala> Foo.bar(1,2,"three")
<console>:22: error: too many arguments for method bar: (values: Values)Unit
Foo.bar(1,2,"three")
^
答案 1 :(得分:1)
修改强>
永远不会选择var-args隐式,因为重复参数在类型方面不是真正的一等公民...它们仅在检查方法的适用性时才存在。
所以基本上,当你调用Foo.bar(1,2,3)
时,它会检查bar
是否用变量参数定义,并且因为它不是,所以它不适用于参数。它不能再进一步了:
如果你用一个参数调用它,它会寻找从参数类型到期望类型的隐式转换,但是因为你用几个参数调用,所以存在一个arity问题,它没有办法可以使用隐式类型转换将多个参数转换为单个参数。
但是:有一种使用自动元组的解决方案。
Foo.bar(1,2,3)
可以被编译器理解为
Foo.bar((1,2,3))
这意味着像这样的隐式工作:
implicit def fromInts[T <: Product](t: T) = Values()
// or simply
implicit def fromInts(t: Product) = Values()
问题在于获取参数的唯一方法是通过t.productIterator
,它返回Iterator[Any]
并需要进行转换。
所以你会失去类型安全;这将编译(并在使用时在运行时失败):
Foo.bar("1", "2", "3")
我们可以使用Scala 2.10的隐式宏使其完全类型安全。宏只会检查参数是否确实是TupleX[Int, Int, ...]
并且只有在通过该检查时才使其自身可用作隐式转换。
为了使示例更有用,我更改了Values
以保留Int
个参数:
case class Values(xs: Seq[Int])
object Values {
implicit def fromInt (x : Int ) = Values(Seq(x))
implicit def fromInts[T<: Product](t: T): Values = macro Macro.fromInts_impl[T]
}
使用宏实现:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object Macro {
def fromInts_impl[T <: Product: c.WeakTypeTag](c: Context)(t: c.Expr[T]) = {
import c.universe._
val tpe = weakTypeOf[T];
// abort if not a tuple
if (!tpe.typeSymbol.fullName.startsWith("scala.Tuple"))
c.abort(c.enclosingPosition, "Not a tuple!")
// extract type parameters
val TypeRef(_,_, tps) = tpe
// abort it not a tuple of ints
if (tps.exists(t => !(t =:= typeOf[Int])))
c.abort(c.enclosingPosition, "Only accept tuples of Int!")
// now, let's convert that tuple to a List[Any] and add a cast, with splice
val param = reify(t.splice.productIterator.toList.asInstanceOf[List[Int]])
// and return Values(param)
c.Expr(Apply(Select(Ident(newTermName("Values")), newTermName("apply")),
List(param.tree)))
}
}
最后,像这样定义Foo
:
object Foo {
def bar(values: Values) { println(values) }
}
您可以使用与重复参数完全相同的语法进行类型安全调用:
scala> Foo.bar(1,2,3)
Values(List(1, 2, 3))
scala> Foo.bar("1","2","3")
<console>:13: error: too many arguments for method bar: (values: Values)Unit
Foo.bar("1","2","3")
^
scala> Foo.bar(1)
Values(List(1))
答案 2 :(得分:0)
规范仅指定函数内 中重复参数(varargs)的类型:
方法中这种重复参数的类型是序列类型scala.Seq [T]。
它不包括其他地方的类型。
所以我假设内部的编译器 - 在某个阶段 - 不能匹配类型。
从这个观察(这不编译=&gt;“双重定义”):
object Values {
implicit def fromInt(x: Int) = Values()
implicit def fromInts(xs: Int*) = Values()
implicit def fromInts(xs: Seq[Int]) = Values()
}
似乎是Seq []。所以接下来的尝试是让它与众不同:
object Values {
implicit def fromInt(x: Int) = Values()
implicit def fromInts(xs: Int*) = Values()
implicit def fromInts(xs: Seq[Int])(implicit d: DummyImplicit) = Values()
}
这个编译,但这并没有解决真正的问题。
我发现的唯一解决方法是将varargs明确转换为序列:
def varargs(xs: Int*) = xs // return type is Seq[Int]
Foo.bar(varargs(1, 2, 3))
但这当然不是我们想要的。
可能相关:隐式转换函数只有一个参数。但是从逻辑(或编译器的临时)的角度来看,在varargs的情况下,它也可能是多个。
至于类型,this可能是有意义的
答案 3 :(得分:0)
这是一个使用重载的解决方案(我不想这样做)
object Values {
implicit def fromInt (x : Int ) = Values()
implicit def fromInts(xs: Seq[Int]) = Values()
}
case class Values()
object Foo {
def bar(values: Values) { println("ok") }
def bar[A](values: A*)(implicit asSeq: Seq[A] => Values) { bar(values: Values) }
}
Foo.bar(0)
Foo.bar(1,2,3)