5分でわかる Scala の魅力 – 関数型は難しそうだけど挑戦したい人に

Scala という言語をご存知だろうか。

使ったことがない方にとっては、難しそうで取っつきにくい言語というイメージがあるかもしれない。

確かに全てを知ろうと思うと難しいが、決して取っつきにくい言語ではない。関数型の特徴を持ちつつ、オブジェクト指向も扱えるというむしろ親切な言語である。

特に「オブジェクト指向に慣れ親しんでいるが、関数型にも挑戦してみたい」というエンジニアにはマッチするだろう。

Scala とは

1. 汎用言語はスケーラブルでなくてはならない。同じ概念で、小さいプログラムも大きなプログラムも記述できるべきである。

2. スケーラビリティは関数型言語とオブジェクト指向言語の2つのプログラミングの概念を統合し、一般化することにより実現できる。

Scala – Wikipedia

Wikipedia にある通り、 Scala の最大の特徴はこの2点、「スケーラブル」と「関数型とオブジェクト指向の融合」だ。

そして、「スケーラブル」とは「シンプルに書ける」とイコール、というのが個人的な考えだ。シンプルであればあるほどスケールしやすい。

Scala の代表的な機能には以下のようなものがある。これらはいずれもシンプルに書くための機能だ。

  • 柔軟な型推論
  • case class
  • match 式によるパターンマッチング
  • if などのほとんどの要素が式

しかし、 5分でわかる と銘打った以上、これらを全て説明することはしない。

Scala らしく、特に私の好きな機能である implicit について紹介する。

Implicit (暗黙)機能

私の知る限りでは、これは Scala に特有の機能で、ユニークな概念だ。

同じ implicit というキーワードを使うが、以下の2つは別のものだ。

Implicit Parameters (暗黙引数)

Implicit Parameters は関数の引数渡しを省略できる機能だ。

DB にモデルを insert するコードがあったとする。簡略化のためにコードを一部省略している。

implicit を使わずに書くとこうなる。関数を呼び出すたびに session を渡しているが、これを書かなくていいようにしたい。

def insertMyModel(model: MyModel)(session: Session): Unit = {
  println(s"insert $model")
}

val session = DB.openSession()

insertMyModel(a)(session)
insertMyModel(b)(session)
insertMyModel(c)(session)

関数の引数宣言に implicit を追加する。

def insertMyModel(model: MyModel)(implicit session: Session)

渡す引数の宣言にも implicit を追加する。

implicit val session = DB.openSession()

こうすることで関数に session を明示的に渡さなくても、暗黙的に渡すことができる。

insertMyModel(a)
insertMyModel(b)
insertMyModel(c)

Implicit Classes (クラス拡張)

Implicit Classes は既存のクラスを拡張する機能だ。

ユーザーが定義したクラスはもちろん、 String などの標準クラスを拡張することもできる。

動的型言語では多く見かける機能で、モンキーパッチと呼ばれることもある。使い方次第では危険なモンキーパッチだが、 Scala においては import したスコープ内のみ有効になるため安全に使うことができる。

これは String に shout メソッドを追加する例だ。

// RichString.scala

implicit class RichString(base: String) {

  def shout(level: Int): String =
    base + "!" * level

}

通常のメソッドと同様に呼び出すことができる。

import RichString.RichString

println("Hello".shout(1)) // Hello!
println("Hello".shout(10)) // Hello!!!!!!!!!!

演算子をオーバーライドすることもできる。

// RichString.scala

implicit class RichString(base: String) {

  def /(position: Int): (String, String) =
    base.splitAt(position)

}
// RichString.scala

implicit class RichString(base: String) {

  def /(position: Int): (String, String) =
    base.splitAt(position)

}
import RichString.RichString

println("ABCDEFGH" / 4) // (ABCD, EFGH)

まとめ

いかがだっただろうか。少しでも Scala の魅力を伝えられたら幸いだ。

もし試してみたくなったら、 Scastie のような Web 上で試せるサイトをおすすめする。

また、疑問があれば遠慮なく下の欄からコメントを頂けると嬉しい。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です