MicroAd Developers Blog

マイクロアドのエンジニアブログです。インフラ、開発、分析について発信していきます。

新卒が初めてScalaを触って詰まったところと解決方法

f:id:microad-developer:20181207175141p:plain

18新卒でマイクロアドに入社した山城です。

今回はScalaを一切触ったことのない僕が、いきなりScalaで開発を初めて困ったところと、どうやって解決していったのかをセットにして紹介したいと思います。

初心者がどこで詰まるのかというところ、どのようにして解決したのかを見ていただいて、新人にScalaを教える際の参考にしていただけたらと思います。

目次

前提

前提として、僕がどれくらいプログラミングができるのかを話します。

入社半年の新人で、普段はJavaを使って既存のプロダクトの改修や、新規プロジェクトのサーバーサイドからフロントまでを書いています。Scalaは名前だけ知っているくらいでした。

プログラミング自体は大学で学んでいましたが、実務的なプログラミングはマイクロアドに入ってから始めました。

そんな新人があるとき、広告トラッキングタグから流れるデータのストリーミング処理を開発することになりました。

開発といってもストリーミング処理の大部分はすでに開発されており、僕が行うのは既存の処理を改良、修正することだったのですが、そのストリーミング処理にScalaが使われていたのです。

マイクロアドではプロダクトのScala化が一部進められていますが未だJavaが大半を占めている都合上、Scalaを書ける人は一部に限定されており、手取り足取り教えていただくこともできないので、自分で勉強しつつの開発となりました。

新人がScalaで困ったこと一覧

ここから、そのストリーミング処理開発の際にScalaに触れて困ったこと、その時どうやって解決したのかを書いていきます。

Scalaってそもそもなに?

まず第一に「Scalaってなに?」というところから始まりました。

  • 「JVMを使っているからJavaと互換性があってスムーズに移行ができる」
  • 「Scalaはいいぞ〜」
  • 「これからの時代はScalaだ!!」

と社内で先輩方が嬉しそうに話しているのを聞いてはいたので、Scalaという名前は知っていましたが、Javaと比較して何が異なり、何が嬉しいのかがわからない状態で開発に着手しました。

「Javaと互換性があるっていうし、何とかなるだろ」 と思って既存のコードを読んでみるとわからない。

特にOptionでラップされている値が追えなかったり、match式が読めなかったり、ListがJavaと少し異なっていたりと、知識0では太刀打ちできませんでした。

開発期間の都合上、1から勉強する時間はなかったので、ドワンゴオリジナルの、新卒エンジニア向けの研修資料やコップ本を流し読みしつつ既存のコードを読むことで少しずつ理解できるようになりました。

JavaでかけてしまうのでJavaで書いてしまう

二つ目は、Scalaらしい書き方を理解しないで書き始めているので「これJavaの書き方だよね?」というコードを量産してしまう点です。

ここはScalaを書ける先輩にレビューをしていただくことで「Scalaらしい書き方」を理解できました。

この時の先輩のレビューが非常に素晴らしく

  • ここがダメ
  • こういう理由だから
  • こうするといい(具体例付き)

という構成でレビューを書いてくださいました。

特に最後の「こうするといい(具体例付き)」がとてもありがたく、新人からすると「こういう理由でここがダメ」で終わってしまうと、良い書き方がわからないからScalaらしくないコードになってしまっているのに、ダメとだけ言われてもどう改善したら良いのかわからない…と手詰まり感を覚えるのではじめに具体例付きで良い書き方を教えていただけたのはとてもありがたかったです。

それ以降のコードでは教えていただいたことを活かして、以前よりScalaらしいコードをかけるようになった気がします。

Optionの扱いにはじめ戸惑う

Scala一番の特徴と言っていいOptionにももちろん戸惑いました。

値が存在するときはSomeクラス、値が存在しないときはNone。値が存在したときはSomeからgetして値を取り出せるけど、値がない場合はNoneにgetをすることになってエラーになるからgetはしないほうがいい。

じゃあどうやって値を取り出すの?

というのが初見の感想でした。

def findID(record: LogRecord): Option[Long] = {
        # idsはMap型
    ids.get(record.id.get)
}
def findID(record: LogRecord): Option[Long] = {
    record.id.flatMap(s => ids.get(s))
}

上のコードは僕が書いたコードで、下が先輩が指摘とともに修正案としてくださったコードです。

LogRecord.idに対応するidをDB上のデータから取得するというメソッドなのですが、僕が書いたコードはgetにgetを重ねています。

LogRecordはトラッキングタグから入ってくるデータを保持しています。タグから流れるデータはきちんと必要なデータが揃って入ってくるとは限らないので、getしてしまうとデータがない場合にNone.getすることになり例外が発生します。

こう言った場合はflatMapなどを使って値を取り出して加工することで、None.getを防ぐことができます。

他にもforeachやmap、match式,、filterを通して値を加工、処理するということを後から自分で学びましたが、Optionの扱いについて一番初めは具体例付きで学ぶのが理解しやすくておすすめです。

Scala特有のコレクションの扱いに戸惑う

Scalaのコレクションライブラリの処理の仕方がJavaとは異なっていてその扱い方も少し戸惑いました。

ScalaのListに向かい合った時に、落ち着いて振り返るとJavaのListもキチンと理解しているか怪しくなってJavaと、Scalaのコレクションについてググって比較することで二つの言語の違いを学びました。

下にJavaとScalaのコレクションをザックリまとめると

Java・Scala 概要
Set・Set 重複要素の無いコレクション
List・Buffer 順序付けされたコレクション
Map・Map key-valueマッピングしたコレクション

となっています。

Scalaのコレクションの特徴はimmutable(不変),mutable(可変)が存在することです。Scalaでは基本的にimmutableを使い、Javaライクに書きたいなどの場合にはmutableを使います。

コレクションの加工の仕方もJavaとScalaで異なります。

Javaではコレクションの要素を加工したり、取り出したりする場合は基本的にfor文を回して取り出し加工するのが一般的かと思いますが、Scalaはコレクションにforeachやmap等のAPIが用意されており、それを用いて加工する方法をとります。

まとめ

以上がプログラミング初心者がScalaを初めて触って困ったことと、実際に行った解決方法でした。 結論としてはやっぱりレビューが一番効果的だなということです。

できる人に聞くのが答えがパッと返ってきて理解が進むのが早く助かるのですが、

  • 相手の時間を取ってしまう
  • 自分で調べる、学ぶ力が養われない

と言ったデメリットもあるので、レビューをいただいたときに出てきた自分の知らないメソッドや言葉を頼りに自分で学んでいく姿勢も併せて持っていきたいです。