バックグラウンド
私は最近、初心者の Scala ミートアップに参加し、メソッドと関数の違いについて話しました (こちらでも詳しく説明されています)。
例えば:
scala> val one = 1
one: Int = 1
scala> val addOne = (x: Int) => x + 1
addOne: Int => Int = <function1>
これは、vals が整数型だけでなく、関数型も持つことができることを示しています。scala repl でタイプを確認できます。
scala> :type addOne
Int => Int
オブジェクトとクラスでメソッドを定義することもできます:
scala> object Foo {
| def timesTwo(op: Int) = op * 2
| }
defined module Foo
メソッドには型がありませんが (むしろ型シグネチャがあります)、それを関数に持ち上げて、それが何であるかを確認できます。
scala> :type Foo.timesTwo
<console>:9: error: missing arguments for method timesTwo in object Foo;
follow this method with `_' if you want to treat it as a partially applied function
Foo.timesTwo
^
scala> :type Foo.timesTwo _
Int => Int
ここまでは順調ですね。関数が実際に apply メソッドを持つオブジェクトである方法と、これを示すために式を非構文的に sugarify する方法についても話しました。
scala> Foo.timesTwo _ apply(4)
res0: Int = 8
scala> addOne.apply(3)
res1: Int = 4
私にとって、これは構文が実際に何を意味するかを内面化できるため、言語の学習に非常に役立ちます。
問題のある例
しかし、特定できない状況に遭遇しました。たとえば、文字列のリストを考えてみましょう。基本的な Scala コレクションと関数型プログラミングのものを示す値に関数をマップできます。
scala> List(1,2,3).map(_*4)
res2: List[Int] = List(4, 8, 12)
さて、List(1,2,3).map() の型は何でしょう? repl で同じ :type トリックを行うと思います:
scala> :type List(1,2,3).map _
<console>:8: error: Cannot construct a collection of type Nothing with elements of type Nothing based on a collection of type List[Int].
List(1,2,3).map _
^
API 定義から、署名が次のようになっていることがわかります。
def map[B](f: (A) ⇒ B): List[B]
しかし、完全な署名もあります:
def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That
質問
だから、私がよく理解していないことが2つあります。
- 通常の関数リフト トリックが List.map で機能しないのはなぜですか? 何が起こっているのかを示すために、誤ったステートメントを非構文糖化する方法はありますか?
- メソッドを持ち上げられない理由が完全な署名の「暗黙的」によるものである場合、正確には何が起こっているのでしょうか?
最後に、REPL から型と署名の両方を検査する堅牢な方法はありますか?