実戦での Scala 〜 6つの事例から知る Scala の勘所〜 - 実戦での Scala | Doorkeeper
こちらのイベントで発表してきました。
発表内容について
スライドを公開しようかと思ったのですが、スライド自体にはあまり情報を載せていなかったので、 簡単にblogエントリとして書いておこうと思います。
ぼくはこれまで、今の会社と前職と、2社にまたがってScalaを用いたプロダクト開発にかかわっています。 人材流動性の高い業界ですから、複数の会社での開発経験などは特に珍しくも無いのですが、Scalaという言語に限ってみれば、わりとまだ事例は少ないのではないかな、と思っています。
そこで、2社でのそれぞれの文化の違いなどから、Scalaとしての扱い方にどのような差異があるか、比較すると面白い結果になるのではないか、と思って今回の発表テーマとして取り組んでみました。
2社の文化的な差異
前職は、Scala導入以前は元々Javaでの開発を中心としており、Javaに精通したプログラマを中心としたチームでScalaを導入しました。 一方、現職のはてなは、Perlに精通したプログラマを中心としたチーム構成になります。
Javaを得意とするエンジニアたちからなるScalaプログラムは、初期の頃はどうしてもBetter Java的な書き方が中心になります。その中で、開発の知見を蓄積する過程において、少しずつ関数プログラミングのスタイルを身につけていきました。そのような事情から、前職で書かれたコードは、それが書かれた時期によってコード全体の雰囲気が大きく変わります。 Scalaを導入して2年以上たち、そもそもScala2.9.x、Scala2.10.x とScalaのバージョン自体も開発の最中で上がっているということもその一因といえるでしょう。
少しコード例を出します。
case class Child(id: Long, name: String, maybeValue: Option[Int]) case class Parent(id: Long, name: String, maybeChild: Option[Child])
このように、ケースクラスが親子関係になっており、子のケースクラスにはOption[Int]
型のフィールドがあります。
ここで親のケースクラスを起点として、最終的に子のケースクラスからOption[Int]
型の値を取り出したい場合、Scalaではいくつかの方法が考えられます。
match式を用いる例
val value = parent.maybeChild match { case Some(c) => { c.maybeValue match { case Some(v) if v > 0 => v case _ => ??? //エラー } } case _ => ??? // エラー }
Better Java時代には、まださほど関数プログラミングが手に馴染んでいない状態ということもあり、この場合match式を用いて値を取り出すコードが多く見られます。僕も当時は実際にこう書いていました。
for式を用いる例
val value = (for { c <- parent.maybeChild v <- c.maybeValue if v > 0 } yield v).getOrElse(???)
学習が深まり、Scalaに対する知見が増えていくにつれ、もう少しシンプルに書く方法が手に馴染んてきます。 それがこのようにfor式を使う例です。
こちらの方が、前述のmatch式と比べてコードのネストがフラットであり、見通しが良いです。
これはあくまでも一例ですが、JavaからScalaに移行したチームでは、このような形でそのコードが書かれた時期によって、全体的な雰囲気に差異が出ていました。
一方で、Perlに精通したプログラマを中心としたチームで導入されたScalaは、全体的にコードに統一感があります。 さきほどの例でいうと、最初から、for式を使って書かれている、という雰囲気なのです。
同じ日の発表で、ビズリーチの竹添さんもJavaよりもLLからの方が入りやすい様子、と仰っていました。(http://d.hatena.ne.jp/takezoe/20150222)
このあたりは、LL言語の方がはやくからラムダ式などの関数プログラミングのエッセンスが取り入れられており、プログラマの関数プログラミングに対する習熟度に差異があるのかもしれないな、と思います。
誤解のないように言っておきたいのは、これはどちらが良くてどちらが悪い、という話ではありません。 Scalaという言語は、元々のチームの文化によって多様な手順を踏んで導入することができる言語だといえます。
発表のまとめ
このような感じで、2社で経験した違いをいろいろ比較してみようと取り組んだのですが、内容を整理するにつれ、思ったほど差異が無い、ということに気づきました。
先ほどの竹添さんの発表のように、マイクロサービスというアプローチであれば、大きな違いがありそうです。あるいは、前職でもぼくがいたのとは別のチームがSkinny Frameworkを用いて開発しており、そこも観測範囲に含めるともう少し違う結果になったでしょうか。 とはいえ、ぼくが経験したチーム間では、両者ともPlayFrameworkを使っていた、ということと、ぼく自身の力不足もあってか、案外差異が無いな、というシャープさに欠ける結論になってしまいました。
とはいえ、この結論をポジティブに捉えるなら、Java -> Scala、 Perl -> Scalaという異なるアプローチで取り組まれたプロジェクトであっても、最終的には違和感無くプロジェクトを横断できる、ということでもあります。
確かにScalaは学習コストは高いですが、ある程度の習熟度を超えると、プログラマにとっての適合度は会社に依存しない、と言えるでしょう(やや強引な結論であることは自覚しつつ書いています)。
イベント全体の感想
今回のイベントは、本当に楽しいイベントでした。個人的に、Scalaコミュニティに長く関わっているので、単純に顔見知りが多い、というのも楽しさの要因のひとつですが、「同じ顔ぶれに会える」というのはScalaもコミュニティとして成熟したきたということなのかな、と思います。
今週末には、今年のScalaMatsuriのキックオフが開催され、コミュニティとしても大きな動きをみせようとしています。 (第1回 ScalaMatsuri 2015準備委員会Kick Off meeting - Japan Scala Association | Doorkeeper)
エムスリーの瀬良さんの発表でも、3年前に比べてScalaはずいぶん成熟した、ということが語られていました。 (Scala が支える医療系ウェブサービス #jissenscala)
その一方で、竹添さんからの、実感として思っていたほど普及しておらず、採用が非常に厳しい、という意見もあります。
課題もいくつかある状態ですが、ぼくとしては引き続きScalaコミュニティに貢献していきたいな、と改めて思わせてくれるイベントでした。
2/23 01:21 追記
http://t.co/h9GAoNMOIr
daiksyさんのあの例、あれだとforでなく
parent.maybeChild.flatMap(_.maybeValue).filter(_ > 0)
と書いたほうが変数名省略できるので個人的にはこう書く、という無粋なツッコミを
— Kenji Yoshida (@xuwei_k) February 22, 2015
val value = parent.maybeChild.flatMap(_.maybeValue).filter(_ > 0)
たしかに、この例だとこの方がよいですねw
ご指摘ありがとうございます。