kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scalaでimplicits呼ぶなキャンペーン

  • はじめのはじめに

    • implicitsは可読性が…という人が居たら、この記事へのリンクを教えてあげていただければと思います
  • TL;DR

    • Scalaには俗に「implicits」と呼ばれる機能がある
    • 実際にはそれらは3つの機能をまとめて指す用語である
    • それぞれの機能は全く異なる
    • 暗黙の型変換は現代では無視してよし
    • 拡張メソッドは、既存の言語でそれを持つのと同じ程度の簡単さである
    • 型クラスとしての用法は、型クラスがある言語を知らないとやや難しいが、積極的に使う価値がある機能である
    • 「implicits」は異なる3つの機能を一つにまとめた呼び方に過ぎず混乱を招くので、使うのをやめよう(個々の機能名で呼ぼう)。また、暗黙の型変換は無視して良いし、拡張メソッドはよく知られているので、型クラスとしての用法にフォーカスして良い
  • はじめに

Scalaには俗にimplicitsと呼ばれる機能(群)があります。(群)と書いたのは、implicitsというのは実は単機能ではなく複 数の機能を指すからなのですが、このことがScalaに関する理解を妨げ、また、Scalaのimplicitsを難しい、黒魔術である、という印象に貢献していると確信しています。

そこで、implicitsに見られる3つの機能群に対して、適切な名前をあたえることでむやみに恐れる必要がないことを示したいと思います。

  • 暗黙の型変換(implicit conversion)

皆さんがimplicitsと聞いたときに最も多く思い浮かべるであろう機能です。が、忘れてください。2017年どころか数年前から、もうこの機能は好ましいものとみなされなくなっており、最近では、Javaのコレクションとのimplicit conversionを提供する標準ライブラリJavaConversionsは非推奨になっています。本当に例外的な目的に限ってのみ使用しても良い機能です。再度いいますが忘れてください。覚える必要がありません。

  • 拡張メソッド(enrich my library)

()内はScalaで従来どう呼ばれていたか、という呼称ですが、一般的な呼称として拡張メソッドとして読んで問題ないでしょう。たとえば、整数nにn回繰り返すtimesメソッドを追加するのは

implicit class RichInt(self: Int) {
  def times(block: => Unit): Unit = {
    var n = 0
    while(n < self) {
      block
      n += 1
    }
  }
}

と書くことで実現できます。このメソッドは、Intをレシーバとする(self: Int)ので、

3.times {
  print("A")
} // => AAAを表示

のように呼び出すことができます。

実は、Scala 2.9以前はより冗長な記法を使う必要がありましたが、Scala 2.10(4年以上前にリリースされたバージョン)からはこの書き方で、Int型にtimesメソッドを生やすことができるようになりました。新しくScalaを始める人はこの書き方だけ覚えていれば(だいたい)大丈夫です。いちいちRichIntと名前を付けるのが煩わしいと感じるかもしれませんが、大した問題ではありません。余分な情報はRichIntの名前の部分のみですから。

拡張メソッドを持つ言語には、C#、Kotlin、Swift(条件によるインポートができないのでちょっと違いますが)があります。これらの言語で拡張メソッドの有害性をうたう人はあまり居ないので、この機能を問題にする必要はないでしょう(自分は同程度に「問題」だとは思うのですが、一般的にはそういう認識だろうと感じています)

  • 型クラス(type class pattern)

最後の一つである型クラスとしての使い方は、とても有用である一方で、若干難しいと感じられるものかと思います。 ここでは、その威力を示すにとどめて、詳細な解説は別のページを探してもらうことにしようと思います。Scalaのコレクションにsumメソッドというのがあります。

これは、コレクションの要素を全て「足し合わせて」その合計値を返す、というメソッドです。

たとえば、

List(1, 2, 3, 4, 5).sum // => 15

でも

List(1.5, 2.5, 3.5).sum // => 7.5

でも同様にメソッドが動作するという点です。sumメソッドが1バージョンしか定義されていないにも関わらず、です。また、自分で有理数型を定義した場合にも、(定義を一切変えずに)このsumメソッドを利用することができます。この機能が非常に強力であることが理解できたと思います。

型クラスとしての使い方は有用性が非常に高く、積極的に使う価値がある機能です。もちろん、きちんとしたドキュメンテーションは必要ですが、あまり恐れる必要はありません。

  • まとめ

implicitsと俗に言われる3つの機能について別個に解説しました。もう「implicits怖い」と言うのは止めましょう。そのような機能は「存在して」おらず、3つの機能が便宜上そう呼ばれているだけなのですから。さらに、暗黙の型変換は無視すべきですし、拡張メソッドは従来いくつもの言語に存在するのですから怖がるほどのものではありません。型クラスとしての使い方のみにフォーカスして理解すればそれで事足ります。これで、もう「implicits怖い」と言う必要はなくなりましたね?