petitviolet_blog

@petitviolet blog

近況

2019/06/30を持ってFringe81株式会社を退職します。

2015/04/01で新卒入社したので4年と3ヶ月の在籍ということになりました。
お世話になった方々、本当にありがとうございました。

以下は振り返りのためのチラ裏。

なんでやめるの

4年間仕事して慣れてきた+自分のライフステージが変わり、自分の成長速度が鈍化してきていることにふと気付いたので環境を変えたいなというのが大体の理由。
もちろん完璧な企業はないので細かいのも挙げ始めればないとは言わないが、自分にとっては些末なことが多い。
給与とか待遇は不満ないし、人間関係で悩んだとかも少なくとも自分にはなくて居心地の良い会社だった。
巷でいわれるような給与アップを転職理由にはしないくらい、ちゃんと評価して反映してくれていたので幸せだったと思っている。

Rebuild.fmで4年周期くらいで転職したくなるよねみたいな話があって、なるほどそういうものなのかと勝手に共感してた。

なにしてたの

時系列だとこんな感じ。

  • 2015.04~2015.10: Android(ちょっとiOS)でのSDK開発
  • 2015.10~2016.12: アドプロダクトAの開発
  • 2016.12~2017.04: 旧プロダクトのJavaScalaへのリプレース
  • 2017.04~2018.05: 広告運用サポートツールの開発
  • 2018.04~2019.05: アドプロダクトBの開発

新規事業を含めて複数プロダクトに関わることが出来ておいしい経験だった。 技術的な領域も幅広く触れたし、いろんなシステムで障害対応したのも振り返ればいい経験だったと思っている。

スマホ開発エンジニアだったのがサーバサイド開発に移って、そっちの魅力に取り憑かれたのでスマホ開発なりフロントエンドをメインにすることは少なくともしばらくは無さそう。 そしてScalaにはまったおかげでプログラミング力はずいぶん成長したように思う。余談だがついにFreeモナドを実戦投入することはなかった。
また、組織的に「設計」を重視しているのもあり、エンジニアとして幅広く力が身についたと感じる。

開発以外だと採用, インターン, 研修カリキュラム作成, 組織内のコミュニティ活動的なことをしたり。 「何を」「どうやって」作るかも大事だけど、「誰と」作るかも大事だというのに気付かせてくれたなぁと。

課外活動的なものだと、ScalaMatsuriとかScala関西みたいなカンファレンスで登壇できたしGoogle/Microsoftさんとイベントやったりも出来た。 Androidエンジニア時代にはRoppongi.aarを立ち上げたけど、自分が離れてモチベーションとともに自然消滅させてしまったのはちょっと後悔している。

会社はマザーズ上場もしたし、エンジニア組織も入社時は10+人くらいだったのが退職時は50+人くらいになり、オフィスは渋谷(面接時)→六本木ヒルズノースタワー→六本木ヒルズ森タワー→六本木一丁目グランドタワーとバブリー(?)な感じになっていき、THE・急成長する会社を味わうことが出来た。

こんな眺めがいい会社で働くことは今後の人生であるんだろうか。

成長する会社にはそれなりの成長痛もあるんだなというのも見えてよかった。

総じていい経験が出来たし、貴重であろう20代後半を過ごしたことに後悔は無い。
月曜の朝に仕事行きたくない...とかもほぼなかった。子ども出来てからは眠かったけど。

おわりに

というわけで、これからもどうぞよろしくお願いいたします。

ScalaMatsuri2019で発表してきました

「ピュアなドメインを支える技術」というタイトルで発表してきました。

2019.scalamatsuri.org

ScalaMatsuriは2017で発表して以来だったので楽しみにしてました。

petitviolet.hatenablog.com

資料

内容としては関数型プログラミング + CQRSという感じでDDDをどうやってピュアに保って推進するか、というもの。 MonadとかKleisliとかCQRSみたいなウッとなりやすい言葉を出しつつどうやってわかりやすく伝えるかに腐心したので、何かしら伝わっていると嬉しい。

感想

時間はたっぷりあったはずなのに、なぜか直前まで原稿書いたりスライドを微修正したりして他の人の発表を十分に楽しめなかったのを大いに後悔している。。

運営の皆さまも本当にありがとうございました! WiFiも快適でカフェスペースもあり、何も困ることがなかったです。

ScalaMatsuriは今後も参加したいところです。

いい感じにオブジェクトのdiffを取るライブラリ作った

Scalaのオブジェクト同士を比較する際に、==を使って比較することは出来るが、同値かそうでないかしかわからない。 そこで、具体的に何がどう変わったのかをジェネリックに取得するライブラリを作ってみた。

こんな雰囲気で使える。

$ amm
Loading...
Welcome to the Ammonite Repl 1.6.7
(Scala 2.12.8 Java 1.8.0_192)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
@ import $ivy.`net.petitviolet::generic-diff:0.6.0-RC2`; import net.petitviolet.generic.diff._
import $ivy.$                                        ;
import net.petitviolet.generic.diff._

@ case class Hoge(i: Int, s: String)
defined class Hoge

@ Hoge(100, "foo") diff Hoge(100, "bar")
res: DiffResult[Hoge] = DiffResult(List(FieldSame("i", 100), FieldDiff("s", "foo", "bar")))

関係ないけどAmmonite便利。

case classのフィールドで異なる値があればそれがわかるようになっている

ソースコードpetitviolet/scala-generic-diffに公開してあってScala2.13.0-RC2まで無駄に対応してみた。

ユースケース

例えば、オブジェクトのフィールドを更新する処理の中で、実際に値が書き換わっていたらDBに保存するが、値が書き換わっていなければDBへの保存処理をスキップする、などが出来る。 あるいはバリデーションで何も更新されていないよと突き返すことも。

classのequalsをoverrideしてidによる同値チェックで実装しているような場合とか。 DDDのEntityでそういう処理をしていることは多そう。

@ case class User(id: String, name: String) {
    override def equals(obj: scala.Any): Boolean = obj match {
      case other: User => other.id == this.id
      case _ => false
    }
    def updateName(newName: String) = new User(this.id, newName)
  }
defined class User

@ val user = User("user-id", "alice")
user: User = User("user-id", "alice")

こういうuserがいた時に、==での比較するとこうなる。

@ user == user
res: Boolean = true

@ user == user.updateName("bob")
res: Boolean = true

これに対してdiffを取ると、

@ user diff user
res: DiffResult[User] = DiffResult(List(FieldSame("id", "user-id"), FieldSame("name", "alice")))

@ user diff user.updateName("bob")
res: DiffResult[User] = DiffResult(List(FieldSame("id", "user-id"), FieldDiff("name", "alice", "bob")))

このようにフィールドごとに同値判定をして、同じ値かどうかを結果として返却してくれる。

異なるフィールドが存在したかどうかはhasDiffを参照すれば可能。

@ (user diff user).hasDiff
res: Boolean = false

@ (user diff user.updateName("bob")).hasDiff
res: Boolean = true

技術的な話

ここからはライブラリの実装的な話でトピックは以下。

ジェネリックプログラミング

case classのコンストラクタ全体を参照するのにshapelessを使っている。 ジェネリックにフィールド名と値を取得している。

実装はdiff.scalaのこのあたり
case classをHListで表現しつつ、LabelledGenericでフィールド名と値の組を扱っている。

再帰的にフィールドを参照しつつdiffをとっていくような実装となっていて、アイデアcirceから学ぶ GenericProgramming入門パクった 参考にした。

macro

こんな感じにdiffした結果をフィールド名で参照できるようにするためにmacroを使っている。

@ val diffResult = user diff user.updateName("bob")
diffResult: DiffResult[User] = DiffResult(List(FieldSame("id", "user-id"), FieldDiff("name", "alice", "bob")))

@ diffResult.id
res: Field = FieldSame("id", "user-id") 

@ diffResult.name
res: Field = FieldDiff("name", "alice", "bob")

ScalaDynamicsを使ってフィールドを自由に指定することが出来るようにしつつ、内部的にはmacroを使って型安全性を担保している。

ここのアイデアscalikejdbc/SQLSyntaxSupportFeature.scalaを参考に実装した。

ここでフィールドとして存在しない名前を指定するとコンパイルで死ぬ。

@ diffResult.hoge
java.util.NoSuchElementException: field #hoge not found.
  net.petitviolet.generic.diff.DiffResult.$anonfun$field$2(GenericDiff.scala:29)
  scala.Option.getOrElse(Option.scala:138)
  net.petitviolet.generic.diff.DiffResult.field(GenericDiff.scala:29)
  ammonite.$sess.cmd15$.<init>(cmd15.sc:1)
  ammonite.$sess.cmd15$.<clinit>(cmd15.sc)

Dynamicだけどマクロで型安全に実装していてScala便利。

今後の展望

使うのかどうかよく分からないので展望とか今のところ特にない。