3

私はこれを試しましたが、エラーで失敗します:拡張関数のパラメータータイプがありません((x $ 29)=> x $ 29.sum)

誰かがこれが起こる理由を説明できますか?これは、Scalaの型推論が十分に強力ではないということだけですか?

object HelloStackOverflow {
    implicit class Repro[T](val iterable: Iterable[T]) extends AnyVal {
        def foo[A, B, Z](bar: Iterable[B] => Z)(implicit evidence: T <:< (A, B)) =
            bar(iterable.map(_._2))
    }

    List(("a", 1), ("b", 2)).foo(_.sum)
}

(Scala 2.10を使用)

4

1 に答える 1

7

これは、「無名関数の引数の型を完全に知っている必要がある」ためです(Scala言語仕様8.5)。

メソッドが無名関数を受け取るとき、scalaはパラメーターのタイプを知っているという事実を使用して、呼び出し元に無名関数のパラメーターのタイプを省略させます(のx => x+1代わりにx: Int => x+1、またはの_.sum代わりにのようなものを記述できますx: Iterable[Int] => x.sum。これはの1つです。 Scalaでの推論の優れたアプリケーションですが、明らかに、これには最初に無名関数の正確な期待される型を知る必要がありますが、ここではそうではありません。無名関数の引数bar は型Iterable[B]です。Bは自由型変数です。以前のパラメーターリストから推測することはできません(メソッドに以前のパラメーターリストはありませんfoo)。したがってB 、無名関数のタイプを推測する方法はありません。(_.sum)正確なタイプが仕様によって義務付けられていることを知っているため、エラーが発生すると推測できます。

これはかなり論理的です。Scalaでは、無名関数は(他の関数と同様に)オブジェクトです。匿名関数を作成するということは、ジェネリッククラス(拡張Function*)をインスタンス化することを意味します。ここで、関数のパラメーターの型はクラスの型パラメーターとしてエンコードされますFunction*(もう一度読んでください。この文は理にかなっていると思います)。タイプパラメータを完全に指定せずにジェネリッククラスをインスタンス化することは不可能です。関数も例外ではありません。

Impredicativeがコメントで示したように、無名関数のパラメーターのタイプを明示的に指定すると、コンパイルエラーが修正されます。

List(("a", 1), ("b", 2)).foo((a : Iterable[Int]) => a.sum)

あるいは:

List(("a", 1), ("b", 2)).foo((_.sum):Iterable[Int] => Int)

しかし、あなたの場合、無名関数のタイプを明示的に指定しなくても問題を修正するのは簡単なようです。

object HelloStackOverflow {
    implicit class Repro[A,B](val iterable: Iterable[(A,B)]) extends AnyVal {
        def foo[Z](bar: Iterable[B] => Z) =
            bar(iterable.map(_._2))
    }

    List(("a", 1), ("b", 2)).foo(_.sum) // works like a charm
}

おそらく、(上記の例のように、パラメーターのT代わりに)単一の型パラメーターを使用した理由は、実際のコードでは、ペアである必要のないクラス内の他のメソッドがあるという証拠があります。この場合、typeパラメーターを使用して別の暗黙のクラスを作成し、これらの他のメソッドをそこに移行します。すべてのエンリッチメントを同じ暗黙のクラスに入れる必要はありません。ABT <: (A, B)ReproTRepro2T

于 2013-02-26T16:53:15.827 に答える