コードを読み込みScalaの関数型パラダイムを学ぶ - xuwei-kがScalaを学ぶために読んだOSS
数多くのScala関連OSSにコミットを続ける吉田憲治(xuwei-k)さん。その精力的な活動を支える、関数型の知見の源をうかがいました。
オブジェクト指向言語と関数型言語の特徴を併せもつマルチパラダイム言語・Scala。この言語に関連するOSSのコミット履歴には「xuwei-k」というアカウントが頻繁に登場します。今回お話を聞いた吉田憲治(よしだ・けんじ/ @xuwei_k )さん、その人です。
吉田さんはScalaのスペシャリストとして、数多くのScala関連OSSにコミットを続け、2018年、Scalaコミュニティに対する貢献者に贈られる「Phil Bagwell Award」を受賞しています。界隈屈指のコントリビューターとして知られる吉田さんに、Scalaのスキルを研鑽してきた過程や、この言語を使いこなすための秘訣について伺いました。
- 当時の自分には、OSSのコードを読む以外の選択肢がなかった
- 「わからなすぎる」からこそ興味を持った関数型のパラダイム
- Play Frameworkやsbtをうまく活用するために
- 自らの学びと、Scalaのエコシステムを支えるためにPull Requestを出し続ける
当時の自分には、OSSのコードを読む以外の選択肢がなかった
——吉田さんはいつ頃Scalaと出会われたのでしょうか?
吉田 『Scalaスケーラブルプログラミング』という書籍が10年ほど前に日本語に翻訳されたのですが、その本をたまたま手にとったことがきっかけです。僕は当時、社会人1年目でしたから、エンジニアとして仕事をするようになって、早い段階でScalaと出会いました。
その頃は、まだScalaに強くコントリビューションしていたわけではなかったのですが、その後、転職をしたくらいのタイミングで本腰を入れて学ぶようになったんです。新卒で入った会社では組み込みでC言語を書いていましたが、転職先ではScalaを書くようになったので。その後も何度か転職をしていますが、業務ではずっとScalaを書き続けています。
——Scala関連のOSSに携わるようになったのはどうしてですか?
吉田 いまと違って、当時はまだまだScala関連の日本語ドキュメントの数が少なかったんです。各種ライブラリの使い方や良いScalaコードの書き方を学ぼうにも、日本語でアクセスできる情報がほとんどない。日本人のScala界隈で有名な人に質問するにしても、限度がありますから。
それに、かつて僕は英語にも苦手意識があって。読むのはギリギリなんとかなるとしても、英語で質問するのは、ちょっと難しい。だから、英語を読むよりもソースコードを読むほうがまだいいなと考えて、OSSのコードに触れるようになりました。僕の場合は特殊なケースだったので、同じ学習法をいまの若いエンジニアの方にはおすすめできませんが(笑)。
読んでいくと、細かい不具合やtypoがよく見つかるんですよね。そこで、直したいと思ってPull Requestを出す。それがコントリビューションのスタートでした。
その後、関数型プログラミングのライブラリであるScalazにもバグフィックスなどのコントリビューションするようになっていきました。特定のファイルでコンパイルエラーが発生していたので、その修正をしたんです。
Scalaz特有の文化なのか、牧歌的な時代だったからなのかはわからないですけど、Pull Requestを発行せずにメインのブランチに直接pushする方もいて。それが原因で特定の機能が壊れていることがよくあったんですよ。
その頃はCIツールもそれほど普及していなかったので、エラーを検出できませんでした。そういった不具合を直すことからOSSへのコントリビューションを始めていきました。
「わからなすぎる」からこそ興味を持った関数型のパラダイム
——Scalaはどのような特徴を持つ言語だと考えられますか?
吉田 特徴はいくつもあるから難しいですが、あえて挙げるなら、やはりオブジェクト指向と関数型の両方を併せもつ言語であることですね。Scalaの作者であるマーティン・オダースキーは、両者の特性を組み合わせたときに真価を発揮する言語としてデザインしたようです。
しかし、作者の意図がすべてのユーザーに伝わっているかと言うと、そうではなくて。OSSのライブラリ界隈でも、極端に関数型に寄っている方々と、そうでない人たちもいます。Scalaのコーディングは、ユーザーによってかなりスタイルが分かれていると感じますね。
——関数型に寄っているOSSとしては、何が挙げられますか?
吉田 先ほど名前が出たScalazは一例として挙げられます。Scalazはいまでこそ開発が下火になって、ほとんど僕しかコミットしていないですが、かつてはScalaで関数型を扱うためのデファクトスタンダードといえるライブラリでした。Haskellにあるような型クラスやデータ構造を、可能な範囲でScalaに翻訳したようなつくりになっています。
Scalazに触れ始めたころは、あまり関数型についての理解がなく、コードを読んでも意味が全くわからなかったんですよ。ただ、わからなすぎて逆に興味がわいてきた(笑)。「どういう意図でこういうつくりになっているんだろう」と考え始めると面白くなってきて、読み進めていました。関数型のパラダイムを学ぶうえで、Scalazはかなり役に立ちましたね。
当時はScalazを解説している記事もなかったですし、Scalaz本体にもドキュメントはほとんど書かれていませんでした。情報源があるとすれば、機能の元ネタになっているHaskellの情報を学ぶしかない。だからHaskellも勉強するようになっていったんです。
——今の時代に関数型を学びたいという若手がいるとしたら、同じ勉強法をすすめますか?
吉田 正直、僕のやってきた勉強法はめちゃくちゃ体力がいるので、もっと楽をすればいいと思います。関数型を解説する書籍も充実してきましたから。例えば、Scalazに関わっていた方々が書いた『Scala関数型デザイン&プログラミング』を読んでみるといいのではないでしょうか。
——関数型の特徴が顕著なOSSは他にありますか?
吉田 極端に関数型の要素が表れているライブラリとしては、effなども挙げられます。実はちょうど(取材実施の)前日にこのライブラリのコミット権限をもらったので、直近は僕のコミットが大量にありますね。
——これは何を実現するライブラリなのでしょうか?
吉田 Extensible effectsと呼ばれる概念をScalaで実現するためのライブラリです。Haskellの世界では、モナドと呼ばれるパーツを組み合わることで処理を実現します。別の種類のモナドを組み合わせるにはモナド変換子と呼ばれるものが必要になりますが、Extensible effectsはこのモナド変換子に近い機能を実現するための概念のひとつになります。
極端に関数型っぽいコーディングスタイルを用いる必要があるので、使用の難易度もなかなか高いです。初学者の方ですと、コードを読んでも何をやっているか理解しにくいかもしれません。
import org.atnos.eff.all._ import org.atnos.eff.syntax.all._ // useful type aliases showing that the ReaderInt and the WriterString effects are "members" of R // note that R could have more effects type _readerInt[R] = ReaderInt |= R type _writerString[R] = WriterString |= R def program[R :_readerInt :_writerString :_eval]: Eff[R, Int] = for { // get the configuration n <- ask[R, Int] // log the current configuration value _ <- tell("the required power is "+n) // compute the nth power of 2 a <- delay(math.pow(2, n.toDouble).toInt) // log the result _ <- tell("the result is "+a) } yield a // run the action with all the interpreters // each interpreter running one effect program[Stack].runReader(6).runWriter.runEval.run
こういった書き方を実務で用いることは稀でしょうが、「関数型っぽいコード」の参考にはなるかと思います。
——実務でScalaを使う場合には、どのような基準で「オブジェクト指向的な書き方」と関数型的な書き方」のバランスをとるべきでしょうか?
吉田 コーディングスタイルは書き手のスキルに依存するので、「チーム内にどのようなメンバーがおり、各人がどれくらいのScalaないし関数型の知識を持っているか」に応じて考える方がいいと思います。
もし、チーム内にさまざまなスキルレベルの人がいる場合には、ある程度のコーディング規約を決めておく方が、面倒が少ないのではないでしょうか。Scalaはオブジェクト指向の色合いが強い書き方も、関数型の色合いが強い書き方もできるので、どちらのスタイルでもいけてしまうところがあり、エンジニアがあまりに自由にやりすぎると、収拾がつかなくなる可能性があります。
関数型を使うことで効果的なコーディングになるならばいいですが、誰も使いこなせないのであれば使っても意味がないですし、逆効果でさえあります。バランスを取りながら使う方がいいと思います。
Play Frameworkやsbtをうまく活用するために
——Scala関連のフレームワークであるPlay Frameworkや、ビルドツールのsbtを実務で使用される方も多いかと思います。これらを用いるうえで、知っておくといいことはありますか?
吉田 Play Frameworkの標準ライブラリの1つであるJSON Combinatorsには、型クラスと呼ばれるScalaの機能が効果的に使われています。JSONの文字列から特定のデータ型に変換したり、逆に特定のデータ型をJSONに書き出すなどの定義を宣言的に書けることが、Json Combinatorsの特徴です。
val locationReads: Reads[Location] = ( (JsPath \ "lat").read[Double](min(-90.0).keepAnd(max(90.0))) and (JsPath \ "long").read[Double](min(-180.0).keepAnd(max(180.0))) )(Location.apply _) val locationWrites: Writes[Location] = ( (JsPath \ "lat").write[Double] and (JsPath \ "long").write[Double] )(unlift(Location.unapply)) implicit val locationFormat: Format[Location] = Format(locationReads, locationWrites)
例えばJavaの場合、Jacksonなどのライブラリとアノテーションを用いてシリアライズ / デシリアライズを行うケースが多いかと思いますが、そうした処理に型クラスを使うのは典型的なScalaっぽい書き方です。Play Frameworkを使うならば、このライブラリをある程度は使いこなしたほうがいいと思います。
——sbtをうまく使うためのコツはあるでしょうか?
吉田 sbtはその概念やDSLが特殊なので、とっつきにくい印象を持つかもしれません。これまでMavenなどを使ってきて、sbtに慣れていない人にとっては、裏でどのような処理が行われているかわかりづらい部分があります。
利用するコツとしては、もしsbtの内部仕様がよくわからなければ、複雑なことをやると辛いので、sbt上で全てのことをやろうとしない方がいいです。依存ライブラリを取得してコンパイルしてテストしてJarをつくるくらいまではsbtでやった方がいいと思いますが、複雑度の高いタスクをsbtでやろうとすると、わかりにくくなる。
例えば、最近はDockerが流行していてsbtにも専用のプラグインがありますけど、それを無理して使わなくてもいい。sbtに最低限の記述をしたうえで、残りはDockerfileに記述するといった手段をとればいいと思います。
自らの学びと、Scalaのエコシステムを支えるためにPull Requestを出し続ける
——Scalaを学び始めた頃と現在とで、吉田さんのコーディングスタイルは変わってこられましたか?
吉田 徐々に「他の人がメンテナンスできるか」を重視するようになってきました。Scalaはさまざまなスタイルで書ける言語なので、実験的な機能を使い過ぎてしまうと、他の人が理解できなくなってしまう場合があります。そうした「よくわからないコード」が、Scalaの評判を落とす要因になってしまったら、元も子もないですからね。
——なぜ、メンテナンス性を重視するようになったのでしょうか?
吉田 OSSの活動を続けていくなかで、その重要性に気づいたからですね。僕はかなりの数のScalaライブラリをメンテナンスしていますが、「誰かがもともと面倒を見ていたけれど、捨てられてしまったライブラリ」を引き取っているケースも多いんですよ。そうした活動を続けているうちに「引き継ぐ人のことを考えて、ソースコードを書かなければいけないな」と思うようになりました。
——誰かに代わってライブラリをメンテナンスするのは、苦労の多い作業かと思います。なぜ吉田さんは、他の人がつくったライブラリを引き取り続けてきたのですか?
吉田 Scalaという言語のエコシステムが衰退してほしくないんですよ。Scalaはまだまだマイナーな言語なので、ちょっとしたことでユーザーが減ってしまうかもしれない。「Scalaはライブラリがすぐにメンテナンスされなくなる」というイメージがついてしまうと、業務で使いにくい言語になってしまう。
——膨大なコミットは、エコシステムを守る、という意思に支えられていることが大きいのでしょうか?
吉田 エコシステムを守り、コミュニティに貢献する手段として、OSSのソースコードを読んでPull Requestを出すのが一番効率がいい。というのと、自分自身の勉強になるので、結果としてコミットは盛んになっています。
——ただ、コミットを継続していくのもかなり大変なことだと思います。
吉田 OSSに関わる活動を続けるには、技術力と、続けることを苦にしない、という能力も必要になると感じます。僕の場合は、苦ではなかったから続けてこられた、というだけかもしれません(笑)。普段、移動中でもスマホでGitHubを見ているんですよ。ここ数年ほどの間、GitHubを観なかった日はないと思いますし、ヒマさえあればコードを読んでいます。
——吉田さんにとっては、OSSが生活の一部になっているのですね。最後に、吉田さんの考えるScalaを学ぶ意義について教えてください。
吉田 Scalaはさまざまなパラダイムや機能を内包している言語なので、Scalaを学ぶことで多様な概念を学べる、という要素は魅力的だと思います。たくさんの概念に触れておくことで、他の言語を学ぶときにもアプローチしやすくなる。やっておいて損はない言語だと思います。
もっとも、注力して学ぶ言語がScalaである必然性は特にないと思っているんですよ。好きでもない言語を学ぶのは一番の不幸だと思うので、自分が突き詰めたいと思える言語や技術にトライすればいい。僕の場合、それがたまたまScalaだったというだけです。
取材・執筆:中薗昴