7

Is it possible to use the magnet pattern with 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"

?

4

4 に答える 4

3

gourlaysama が既に述べたようにProduct、構文的に言えば、varargs を 1 つに変えるとうまくいきます。

implicit def fromInts(t: Product) = Values()

これにより、次の呼び出しを正常にコンパイルできます。

Foo.bar(1,2,3)

これは、コンパイラが自動的に 3 つの引数を にリフトするためTuple3[Int, Int, Int]です。これは、アリティが 22 までの任意の数の引数で機能します。問題は、どのようにタイプ セーフにするかです。Product.productIteratorメソッド本体内の引数リストを取得する唯一の方法ですが、Iterator[Any]. メソッドが s のみで呼び出されるという保証はありませんInt。これは驚くべきことではありません。署名でInts のみが必要であると実際に言及したことさえないからです。

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 )

メソッド本体の中で、 new methodd を使用しargsて引数リストを取得します。

bars のタプルではないタプルで呼び出そうとするとInt、コンパイル エラーが発生し、タイプ セーフが返されることに注意してください。


更新: 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")
                 ^
于 2013-06-02T18:48:06.003 に答える
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)
于 2013-06-02T17:15:19.807 に答える
0

仕様は、関数から繰り返されるパラメーター (varargs) の型のみを指定します。

メソッド内で繰り返されるこのようなパラメーターの型は、シーケンス型 scala.Seq[T ] です。

他のどこのタイプもカバーしていません。

したがって、コンパイラは内部的に、特定のフェーズで型を一致させることができないと想定しています。

この観察から(これはコンパイルされません=>「二重定義」):

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))

しかしもちろん、これは私たちが望むものではありません。

関連する可能性があります: 暗黙の変換関数にはパラメーターが 1 つしかありません。しかし、論理的な (またはコンパイラの一時的な) 観点から見ると、varargs の場合、それも複数になる可能性があります。

タイプに関しては、これは興味深いかもしれません

于 2013-06-02T14:26:24.060 に答える