WebエンジニアのLoL日記

LoLをプレイしたりLJLの試合を見たりするのが好きなエンジニア。LoLのイベントやパッチノートなど気になった点を記事にしたり、LJLについの記事をかいたりしています。某社でWeb系のエンジニアとして働いているので、技術系の記事もたまに書きます。コンタクトを取りたい場合はtwitterまで。

【ScalaMasturi2018】OE_ujaさん「『暗黙』とうまく付き合うためのTips」メモ

もくじ

自己紹介

implicitとは

Scalaには大きく分けて2つのimplicitがある

Implicit parameter(暗黙の引数) Implicit conversion(暗黙の変換)

Implicit conversionはある意味implicit parameterの一種なのだが、わかりやすくするために分けてある。

implicit parameter とは

Scalaの標準ライブラリのSeqのsortedで使われてる。

Sortedはソートするメソッド

Seq(2, 1, 3).sorted
// -> Seq(1, 2,3)

標準のソートの順番がimplicitで定義されている

implicit conversion

暗黙の型変換

例えばjavaのListとScalaのListに変換するのに暗黙の型変換が使われている。

val jlist: Javaのリスト = Sew(scalaのリスト)

なぜ implicit と呼ばれるか

何が起きているのかコード上からわからない(わかりづらい、ので難しい)

暗黙のスコープというものがある

ということで少しなれるのにハードルがある。

「暗黙の値」が他の暗黙の値を使っていたり、関数の引数に暗黙の値を使っていたり、などで複雑で難しい

このセッションはこの暗黙にどう向き合っていったらいいかを考える。

sortedは何が起こっているのか

Seq(2, 1, 3).sortedについて

scalacでオプションに-Xprint:typer をつけると、自分の書いた式が同評価されているかわかる。sortedには何も引数は私絵いなくても、勝手に何か引数が渡されている。

scala -Xprint:typer で実際に何が呼ばれているかわかる

プロジェクトではどうするか

プロジェクトだと typer つかうとめっちゃ色々出てきてやばい。IntelliJのimplicitまわりの機能は非常に良い。

IntelliJCtrl + Shift + P にバインドされていると、implicit parameter が定義されているところに飛べる。(Community Editionでいける)

// MEMO: scalaのdiってimplicit parameter使えるのだろうか

ある暗黙の値を使って別の暗黙の値を定義している場合などは?

同じく Ctrl + Shift + P 、トグルを開くとさらにその先の暗黙の値が見られる。

Enrich my Library パターン

Scalaにおいて暗黙の型変換を使って実装されるパターン

「私のライブラリをリッチにします」

// TODO: 話が早くてどういうパターンかわからなかったのでぐぐる

Java convertersの例(Enrich my Library じゃない例?)

例えば、JavaのListの引数を渡すところにScalaのListを渡すと勝手にJavaのリストに変換される。勝手に変換されていることに気づきにくいため、バグの温床になる可能性がある。(わかる)

たとえば、自分implicitでasJava関数を定義する。以下のようにScalaのリストとJavaのリストを変換できるようにする。

val list:java.util.List[Int] = List(1, 2, 3).asJava

この場合、コードジャンプで asJava の実装もとに飛べる。

scala.collectionJavaConvertes (deprecated)を使う

Import scala.collectionJavaConvertes
val list:java.util.List[Int] = List(1, 2, 3)

暗黙の型変換になった(asJavaがなくなった)。暗黙の型変換が怒っていることに気づかないしコードジャンプも厳しい。

ので、Enrich my Library パターンを使おう!

sortedのOrdering[Int]は?どこに定義されている

// MEMO: 早くてメモしきれなかったので資料を見る

暗黙の値をスコープに加える代表的な方法

明示的に暗黙の値をimportするか、スコープに直接定義する。

なんの型のコンパニオンオブジェクトに定義したらいいかが問題。

どんなコンパニオンオブジェクトに定義したらいいのか

Scalaのドキュメント読んでもピンとこないので説明します。

Parameterized Type Super Type Package Object

などなど

Implicit classはimportすることでimplicitが定義されるので余計な事故を防げる。

例えば Ordering[User] を定義する

List[User]のsortedのデフォルトを定義するやりかた

object User の中に定義する

Odering[User] を定義する必要がある。Ordering.by を使う

Ordering.by(.id) とか Ordering.by(.name) とか

MapとContramap

Readsのはmapという名前で定義 Writesを定義する場合はcontramapという名前で定義

共変・半変の説明は省略

PlayのJsonのやつのReadsとかWritesとかがそれかな

感想

内容は面白かったけどもりだくさんで少し説明が早い><

// TODO: スライド公開されたら見たほうがいい。あとで公開するとのこと。