Scala

Scalaの=>記号の意味が多すぎるのでまとめた

More than 1 year has passed since last update.

本格的にScalaを勉強し始めたのですが、=>という記号の使用される場面が多くて混乱したので、まとめました。

=>記号の意味一覧

=> という記号はScalaの色々な場面で使用されます。

  • 関数型の表現
  • ラムダ式
  • パターンマッチ
  • 別名でのインポート
  • 引数の名前渡し
  • 明示的に型付けされた自己参照(explicitly typed self reference)
    • @kazznaさんのご指摘で追加しました

では、以下でそれぞれの用法を見ていきます。

関数型の表現

関数を型として表現するのに使用します。
(引数の型) => (返り値の型)という形で使用します。

//Intを引数に取りStringを返す関数funcを、引数に取る関数の定義
def someFunc(func: Int => String): Unit = {/*some code*/}

ちなみに引数がk個の関数型は、内部的にはFunctionk(0<=k<=22)という名前のTraitとして定義されています。

ラムダ式

ラムダ式の定義に使います。
(引数) => (コードブロック)という形で使用します。

//Int型の値を2倍にして返す関数
val double = (a: Int) => { 2 * a }

パターンマッチ

パターンマッチの各ケースと値・処理の紐付けに使用します。
matchブロックの中で、(ケース) => (処理内容)の形で使用します。

//Option[Int]型の引数に値があればその値を、Noneなら0を返す関数
def getOrZero(x: Option[Int]) = match x {
    case Some(num)  => num
    case None       => 0
}

別名でのインポート

インポートしたライブラリを別名で使用する場合に使います。
(ライブラリ名) => (別名)という形で使用します。

# JavaのリストをJListという名前で使用する
import java.util.{List => JList}

引数の名前渡し

引数の名前渡しに使用します。
名前渡しというのは、型を直接引数に指定するのではなく、引数を1つも取らずその型を返す関数を引数にする方法です。

つまりIntを引数に取る関数を書くのに、以下の様な形ではなく

def byValFunc(num: Int) ={/*some code*/}

val result = byValFunc(5)

以下のように書くということです。

def byNameFunc(numFunc: => Int) ={/*some code*/}

val result = byNameFunc{5}

これの何が嬉しいかというと、引数の値の評価を、コンパイル時ではなく実行時に行うことができます。

上の例では何のメリットも感じられませんが、引数の値が非常に計算コストが高く、しかも必ずしも使われるとは限らない場合、パフォーマンス面で威力を発揮します。

明示的に型付けされた自己参照(explicitly typed self reference)

とても意味深な名前ですが、簡単に言うと「thisが指し示す型を指定する機能」です。
クラスのコンストラクタで、self: (指定する型) =>という形で使用します。

以下に簡単な例を示します。
InnerOtherと何ら継承関係がないにもかかわらず、Innerの内部でself: Other =>と宣言しているため、Other型の変数にthisを代入できています。

//外部クラス
class Outer {

  //抽象型を宣言
  type Other

  //内部クラス。Otherとは継承関係はないが………
  class Inner {

    //このように宣言すると
    self: Other =>

    //thisがOther型と認識され、コンパイルエラーも起きない!
    val other: Other = this
  }
}

この機能は、複雑なクラス構造を構築するときに必要になることがあるようですが、私には高度すぎるので、これ以上詳しいことはexplicitly typed self referenceでぐぐってみてください。

=>は関数リテラルのためにある(……?)

こうまとめてみると、=>記号の使用目的は、概して関数リテラルの記述にあるということがわかります。
(パターンマッチの構文も、内部的には関数リテラルの束にすぎないそうです)

最初は色々な用途があって混乱しましたが、実際には統制の取れた糖衣構文であることがわかりました。

追記

別名でのインポートは、見たところは関数リテラルとは関係なさそうです。実は内部的には関数だったりするのでしょうか? Scalaの実装に詳しい方、よろしければご教示ください。

追記の追記

明示的に型付けされた自己参照に関しても、関数リテラルとは余り関係なさそう。
やっぱり、=>演算子の役割多すぎませんかね?