11

私はScalaにこのクラスを持っています:

object Util {
  class Tapper[A](tapMe: A) {
    def tap(f: A => Unit): A = {
      f(tapMe)
      tapMe
    }

    def tap(fs: (A => Unit)*): A = {
      fs.foreach(_(tapMe))
      tapMe
    }
  }

  implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
}

今、

"aaa".tap(_.trim)

コンパイルされず、エラーが発生します

エラー:拡張関数のパラメータータイプがありません((x $ 1)=> x $ 1.trim)

タイプが推測されないのはなぜStringですか?エラーから、暗黙の変換が発生するようです(そうでない場合、エラーは「tapクラスのメンバーではありません」の行に沿って発生しますString)。そして、変換はにならなければならないようです。Tapper[String]つまり、引数のタイプはString => Unit(または(String => Unit)*)です。

興味深いのは、どちらかの定義をコメントアウトするとtap、コンパイルされることです。

4

1 に答える 1

17

6.26.3オーバーロードの解決

まず、引数の形状に基づいて、潜在的に適用可能な関数のセットを決定します

..。

Bに選択肢が1つだけある場合は、その選択肢が選択されます。

それ以外の場合は、S1 、。。。、Smは、未定義の予期される型を使用して各引数を入力することによって取得される型のベクトルです。

の両方のオーバーロードtapは潜在的に適用可能です(引数の「形状」に基づいており、これはアリティおよび型構築子FunctionNを説明します)。

したがって、タイパーは次の場合と同じように続行します。

val x = _.trim

そして失敗します。

よりスマートなアルゴリズムは、各選択肢の対応するパラメータータイプの最小の上限を取り、これを期待されるタイプとして使用できます。しかし、この複雑さは実際には価値がありません、IMO。オーバーロードには多くのコーナーケースがありますが、これは別のケースです。

ただし、単一のパラメーターを受け入れるオーバーロードが本当に必要な場合は、この場合に使用できるトリックがあります。

object Util {
  class Tapper[A](tapMe: A) {
    def tap(f: A => Unit): A = {
      f(tapMe)
      tapMe
    }

    def tap(f0: A => Unit, f1: A => Unit, fs: (A => Unit)*): A = {
      (Seq(f0, f1) ++ fs).foreach(_(tapMe))
      tapMe
    }
  }

  implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)

  "".tap(_.toString)
  "".tap(_.toString, _.toString)
  "".tap(_.toString, _.toString, _.toString)
}
于 2010-07-23T07:21:58.063 に答える