転職して二ヶ月くらい経った
ご無沙汰しています。数年ぶりの生存報告(?)です。 ちょっとふわっとした内容ですが、せっかくなので近況とか(もう3月なのに)今年の抱負?とか残しておこうと思います。
ブログは2019年末を最後に全く更新していなかったですが、適当に振り返ってみると Lens の次は継続モナドや Tagless-final に夢中になったり、 チーム開発で技術的負債と向き合ったりしていてまぁ相変わらずだなぁという感じでした。この辺りの話も完全にタイミング逃してしまったのですが、 そのうち気が向いたタイミングで何か書けるといいですね。いつかは...。
プライベートだとかれこれ5年以上住んでいた都内の狭小ワンルーム賃貸マンションを引き払って地元の茨城県に帰ってきました。 窓を開けていても木々のざわめきや鳥の囀り、時折バイクのエンジンふかしてる音くらいしかしないので至って静かと言っても過言ではないでしょう。 しかし歩いてコンビニにはギリ行けますが、完全に車社会なのでふらっと飲みにはいけないしそもそも最寄駅周辺に良さげな Bar も まともな本屋もないのでストレス発散に目的もなく外出したくなる人間にとっては少々物足りなさを感じていたりもします。 まぁ、どちらにせよ現在の感染症状況的には家から出ない生活一択なのですが。
というわけで本日最終出社でした。約4年半、関係者の皆々様には大変お世話になりました。心よりお礼申し上げます🙇♂️画像は在宅勤務でオフィス撮り損ねたので返却予定の PC です(?) pic.twitter.com/YhmNeoQGp9
— 超ニャーニャーとなくどうぶつ (@AoiroAoino) 2021年12月8日
そんなこんなで適当に過ごしていましたが昨年末に退職してました。 職は変わりましたが引き続き茨城からリモートで Scala 書くお仕事を頑張っています。
アルプ株式会社に入社しました
年明けの1月4日からアルプ株式会社にてお世話になっています。
2018年の創業で、近年お馴染みのサブスクリプションの契約や請求(not 請求書)管理をする SaaS、Scalebase を提供する企業です。 エンジニア界隈的な話題だと Scala で Eff めっちゃ使ってたり ScalaMatsuri の大名スポンサーに名を連ねていたり。 ビジネス面では約一ヶ月ほど前に12.5億円もの資金調達を行うなど、今とても勢いのあるベンチャー企業です。
どんなお仕事しているの?
サブスクリプション関連のコンテキストの設計やドメインレイヤーのコードを改修してたりします。
転職してやりたいことの一つに、複雑なドメイン知識の理解とそれを踏まえての設計から開発に携わりたいというのがありました。 とりあえず一ヶ月程度は全くわからん!!と嘆くことが多かった会計というか売上や請求あたりの知識とそれをベースにした実装などなどですが、 二ヶ月もぼちぼち経過していくつかの機能実装や各種ミーティング、社内勉強会に参加を通して業務知識のスタートラインに立てたような気はします。
ここ数年考えてることではありますが、例に漏れず理解が浅かろうが深かろうが機能として実装に落とし込むのは自分のメインの業務であり、 自身がユーザーではなかったとしても背景や課題感に理解(自分の中で腑に落ちている状態)でなければそもそもドメインエキスパートと深い議論をすることも実装に落とし込むことも叶わなかったり、 例え動くものが作れたとしても真にユーザーに使われるような機能として完成させるのが難しくなってしまうなぁと再認識したり。
相変わらずバックエンドで Scala を書いてはいますが、これまでの toB, toC で非機能要件突き詰めるような開発とはまた違った領域の実装をやっている感じです。楽しいね!
エンジニアリングに関して
大半のメンバーが設計談義が大好きなので雑談でもモデリング会でも議論が始まれば Entity なのか VO なのか、はたまたドメインモデルとして作るべきか否か、 仮にそうだとした場合に既存のモデルとの食い合わせは果たしてメンテ可能かどうかなどなどひたすら話が尽きないという、なかなかに楽しい時間を過ごすことができます。
そんなバックエンドですが、システム全体としては Kubernetes (EKS)の上で大半のアプリケーションが動いていて構成は Terraform で管理されています。CI/CD は Circle CI と Jenkins が併用されていて、裏方作業関連の便利バッチ/ツールなんかもここに集約されています。バックエンドアプリケーションはほぼ全て Scala で実装されていて、 Akka Http がベースの Web アプリケーションや Spark を使ったバッチなんかも存在しており、それぞれのコンテキスト単位でいわゆる Clean Architecture で構成されていて、 それらを束ねて一つのモジュラモノリスを成しています。そして全体を通してEffが積極的に使われています。
Eff に関しては業務アプリケーションでの実用経験はありませんでしたが、Tagless-final と比べて Monad の型クラス制約をかけずとも for 式で合成できるのはもちろん、 ADT を組み立ててそれをインタープリタで実行するといったプログラムと実行の分離が自然と実現されるので、ドメイン層からインフラ層(アダプター層)まで 通して使いやすく感じました*1。 そして、単に使う分には処理を for 式で組み立てて既に提供されているインタープリタ(runFooメソッド)を実行する程度なので、Eff 自体に精通している必要もあまりなくチーム内でのスキル差をうまく吸収でき、 近年の Scala でのアプリケーション開発における(副)作用の扱いどうするか課題における一つの解決策としてとてもアリだなぁと思っている今日この頃です。
そんな Eff の話題も含めて今週末3/4(金)開催のイベントや ScalaMatsuri 2022 のメインセッション登壇など、 社内エンジニアの積極的な情報発信/アウトプットが行われているのもとてもいい文化だなと思ったり。 (宣伝)
https://scalamatsuri.org/ja/proposals/J1647668100
オーバーコミュニケーションの組織と文化
サービス自体やシステムの面白さに惹かれた部分ももちろんありますが、 Biz, Dev 問わず一緒に働くことになる方々を特に魅力的に感じた、という人的な部分もまた大きいです。
全社的に Notion と Slack に情報を集約していて、エンジニアも商談や顧客定例議事録を見たり利用状況やら把握して顧客社名で会話するし、 カスタマーサクセスのメンバーも内部仕様めっちゃ詳しいし SQL ばんばん書きます。こうした部署間の密なコミュニケーションが もはや当たり前に行われている状況はなかなか驚きでした。実際やろうとしてもなかなか難しいですよこれ。 自分が直接お客さんとお話しすることはほとんどないにしても、近いと錯覚するくらいには情報共有が上手く回っているように思います。
当事者意識とコミュニケーションが大切!なんてのは文章にしてしまえばなんて事ない当たり前の話になってしまいますが、 実際一緒に仕事をしていて各々担当している領域 + αに対するラストマンシップ具合もまた飛び抜けてるようにも思います。 もちろん個人的なこれまでの人生の狭い観測範囲でしかないですが、正直なところ何がどうなってこんな面白い文化が醸成されるんだ?と不思議に思ってたりもします。 もちろん褒めです。
ある意味このようなハイコンテキストなコミュニケーションが成立するのは今の人数だけかもしれないし組織が拡大するにつれて綻びが生じるのではないかという懸念もないわけではないですが、 現状の環境を作り上げることができているメンバーならば何だかんだで乗り越えられるのはもちろん、 変化自体が今のフェーズならではの楽しみとさえ思えてしまうかもしれません。そんな強い組織と文化な現状がとても楽しいです。
まとめ
といった具合で近況やら所感やら宣伝やらをだらだら書き連ねましたが、 社内に凄いメンバーが揃っていて負けたくない(?)し、ドメインやら業界商習慣やらの勉強もしつつ リファクタリングやら新機能の設計& 実装やらも担当したりで(自分の中では)大忙しだったりしますが、 とても充実してるしテンション高めでやっているぞ〜〜〜という生存報告でした。
というわけでビジネス的にもシステム開発的にも今めちゃくちゃ面白いフェーズなので興味のある人カジュアルにお話でもしませんか?
という定番セリフで締めておきます。今度こそは定期的にブログ更新できる年にしたい...。
(´-`).。oO(早く気兼ねなく飲みに行ける出社できるようになるといいですね。
Scala秋祭り2019で「Scala における継続モナドの実装と活用」って内容で発表してきた
こんなイベントがあったとさ。
scala-aki-matsuri.connpass.com
資料はこれ。
そういえば、Scala関西Summit 2019 でも登壇するのでよしなに。
以下中身のない話。
やっぱりスライドは Markdown で書けると楽だよねって長年思ってたんですが、最近はレイアウトだの画像貼り付けだのが面倒になったので、 Google Slides に Gist からソースコード貼りまくる感じになった。見た目がいい感じにはできたのでひとまず満足してるんですが、
2019年もぼちぼち終わりますが、スライドにいい感じにソースコード貼る(書く)方法、確立されないですかねぇ...
— Naoki Aoyama (@AoiroAoino) September 14, 2019
Google スライドのアプリはオフラインを主張してるけど、あたしはブラウザ開いて Twitter 出来てるのじゃが
— Naoki Aoyama (@AoiroAoino) September 15, 2019
このあたりはなんとかいい感じになってほしい気がする。 自分でツッコミ入れるのあれですけど、二つ目は普通にバグでは...? (とはいえ約70枚のスライドをハイライトのタグが入ったソースコードで埋め尽くすような利用者が果たしてどのくらいいるのか...
なんかここ最近全然発表も OSS 活動(?)とかブログ書いたりとかしてなかったし、そういう活動を積極的にやりたい気力も色々あってなくなってたんですが、 いざ久しぶりに話をしてみるとやっぱり楽しいので、ぼちぼちいい感じにまたやっていくという気持ちにちょっとなった。今日も睡魔は来ない。
cats.data.ContT を試してみた
Scala Advent Calendar 2018 - 24日目の記事です。
ついに12月でアドカレ始まったかーと思ってたらもうクリスマスイブで自分の番ですよ。早いですね。 今年のブログ更新もどうせこれが最初で最後でしょう。また来年頑張ります...
さて、今回は一ヶ月ほど前に master に入り、v1.5.0-RC1 から使えるようになった cats.data.ContT について少し試した感想を。
ちなみに、継続モナドについては丁度7日に @kazzna さんがContの合成がしたいというタイトルで 公開されていたので、そちらもどうぞ。ContT はそんな継続モナドのモナドトランスフォーマー版です。
ついに Cats にも ContT が
Scala においても継続モナドは実用的なツールの一つです。 自分の観測範囲では例えば、 ActionCont で有名な pab_tech さんの qiita.com jwhaco さんの qiita.com しもむらさんの labs.septeni.co.jp
などなど。特に ActionCont には公私ともにお世話になってたり。
さて「ついに Cats にも〜」って書き出しからもお分かりの通り、Scalaz には結構昔からありました。 ただ、ここ最近に至っては*1 doobie や Monix が内部実装を Scalaz から Cats へ移行するなどしてしまったため、 Web サービスやらなんやらの実用的なアプリケーションを作ろうとした際に、Cats を使って実装されたライブラリとの組み合わせが悪くなってしまったのでした。
そんな状況だったので、各々そもそも使うのを諦めたり、適当に自前実装したり、 はたまた Scalaz の実装コピって置換しただけみたいなライブラリが登場したり*2といった感じの状況だったのが、 ようやく解消されたのかなーといった感じでやったー!みたいな気持ち、のはずでした。
cats.data.ContT を試してみる
以降、登場するサンプルコードは
- Scala: 2.12.8
- Scalaz: 7.2.27
- Cats: 1.5.0
- sbt: 1.2.7
でsbt console
で REPL 起動しています。
では早速、さくっと Option モナドに積んで試してみましょう。なんら意味のないコードですが。 まずは比較の為に Scalaz から。
scala> import scalaz.ContT import scalaz.ContT scala> def cont(s: String): ContT[Option, Int, String] = ContT(f => f(s)) cont: (s: String)scalaz.ContT[Option,Int,String] scala> val c = for { | a <- cont("12") | b <- cont(a + "24") | } yield b c: scalaz.IndexedContsT[scalaz.Id.Id,Option,Int,Int,String] = IndexedContsT(scalaz.IndexedContsT$$Lambda$4064/1553542174@5aeb00bd) scala> c.run(s => util.Try(s.toInt).toOption) res0: Option[Int] = Some(1224)
いいかんじですね。さて、今度は Cats で同じことをやってみます。
scala> import cats.data.ContT import cats.data.ContT scala> def cont(s: String): ContT[Option, Int, String] = ContT(f => f(s)) cont: (s: String)cats.data.ContT[Option,Int,String] scala> val c = for { | a <- cont("12") | b <- cont(a + "24") | } yield b <console>:15: error: could not find implicit value for parameter M: cats.Defer[Option] b <- cont(a + "24") ^ <console>:14: error: could not find implicit value for parameter M: cats.Defer[Option] a <- cont("12") ^
おやおや、Cats の方は失敗しました。なんか暗黙の値が無いよって怒られてますね。
合成するには Defer 型クラスのインスタンスが必要
さて、エラー内容を頼りに ContT の実装を見てみると、 map/flatMap を実行する為には M[_] が Defer 型クラスのインスタンスであることが求められています。
sealed abstract class ContT[M[_], A, +B] extends Serializable { final def run: (B => M[A]) => M[A] = runAndThen protected def runAndThen: AndThen[B => M[A], M[A]] final def map[C](fn: B => C)(implicit M: Defer[M]): ContT[M, A, C] = { // ... } // ... final def flatMap[C](fn: B => ContT[M, A, C])(implicit M: Defer[M]): ContT[M, A, C] = { // ... } }
Defer 型クラスは非常にシンプルで、F[_] の生成の際に中身の生成を遅らせられることを要求します。
trait Defer[F[_]] extends Serializable { def defer[A](fa: => F[A]): F[A] }
適当に確認する限り、この Defer 型クラスのインスタンスは馴染みのあるところで
- cats.Eval[A]
- cats.Free[S[_], A]
- cats.effect.IO[A]
- monix.eva.Task[A]
また、F[_] が Defer のインスタンスに限り、下記もインスタンスになります。
- cats.data.EitherT[F[_], A, B]
- cats.data.OptionT[F[_], A]
- cats.data.IndexedStateT[F[_], SA, SB, A]
- cats.data.Kleisli[F[_], A, B]
といった具合で、サンプルコードの(標準ライブラリの)Option に対しては定義されておらず、 裏を返せばこれ以外には ContT を積めない(積めるけど合成できない)ということになります。えぇ...
ちなみに Defer が必要な理由として、Cats の場合は Monad のインスタンスを定義する際に、 stack-safe であることが要求*3されるので、それの実現の為なはずなんですが、 そのあたりはまた別の記事で。*4
cats.Cont はどこ?
Scalaz v7.2.27*5 の Cont は
type Cont[R, A] = ContT[Id, R, A] object Cont extends IndexedContsTInstances with IndexedContsTFunctions { def apply[R, A](f: (A => R) => R): Cont[R, A] = IndexedContsT[Id, Id, R, R, A](f) }
のように定義されていて、これは (ContT に限らず) Id モナドに積んでやれば元の(?)シンプルな定義を得られるといった具合です。 ところが cats.ContT でやろうとすると、cats.Id に対する Defer 型クラスのインスタンスが存在しない*6ため、定義そのものはできるものの map/flatMap が使えず、for 式での合成ができません。
scala> type Cont[R, A] = ContT[Id, R, A] defined type alias Cont scala> ContT.pure[Id, String, Int](100) res0: cats.data.ContT[cats.Id,String,Int] = FromFn(AndThen$1198828579) scala> res0.map(_.toDouble) <console>:22: error: could not find implicit value for parameter M: cats.Defer[cats.Id] res0.map(_.toDouble) ^
一応、Defer 型クラスのインスタンスでもある cats.Eval を使うことで回避することが可能ですが、果たして。。*7
scala> ContT.pure[Eval, String, Int](100) res2: cats.data.ContT[cats.Eval,String,Int] = FromFn(AndThen$1654564972) scala> res2.map(_.toDouble) res3: cats.data.ContT[cats.Eval,String,Double] = FromFn(AndThen$627616169)
ってな具合で、使う場合はどこでもまずはモナドトランスフォーマー版から始めなきゃいけないのも手間ですね。
まとめ
使いにくいなーと思う箇所はあれど、実際は Monix の Task に積んだり、Eval でも問題なかったりで、実用上はそこまで困らない(はず)って印象です。 何はともあれ "Cats でも継続モナドが使えるようになった" という事実は大きいので、これからじゃんじゃん使い倒していくぞという気持ちです。ありがとうございます。
良いお年を
*1:といっても、数年前ですが...
*2:さすがにどうなんだ...
*3:tailRecM もそうだけど、中の人の方針的にも?
*4:そもそも Monad の制約に tailRecM を強制するのがやっぱり良くないのではって気がしなくもない
*5:https://github.com/scalaz/scalaz/blob/v7.2.27/core/src/main/scala/scalaz/package.scala#L320-L323
*6:ちゃんと調べられておらず、理論的に実装不可か、それとも頑張れば定義できるがまだ入っていないだけなのかは不明なので「現時点の実装ではできない」という事実のみ。それ以外の意味はありません。
*7:cats.Eval は評価を制御できるようにするデータ型で、モナドです。Id モナドとは別物ですが、一番シンプルで近いデータ型のはず...
近況とか
以降の話をハイライトでお伝えします。
この記事は Scala関西Summit 2017 の資料作成に追われる現実逃避息抜きに書いています。
しゅごい #rpscala pic.twitter.com/xanSrA8PxX
— Naoki Aoyama (@AoiroAoino) 2017年6月10日
#rpscala pic.twitter.com/A6RKG6mlNO
— Naoki Aoyama (@AoiroAoino) 2017年6月11日
また一つ歳をとってしまった
— Naoki Aoyama (@AoiroAoino) 2017年6月24日
Scala関西Summit 2017、CFP 採択されたので登壇します!やったぜ #scala_ks
— Naoki Aoyama (@AoiroAoino) 2017年7月9日
昨晩 prime now のおかげで遂に switch が手に入ったのだけど、一通り初期化とコントローラーの付け外し体験しただけで最高にテンション上がった
— Naoki Aoyama (@AoiroAoino) 2017年7月11日
#scalafukuoka 来てます pic.twitter.com/hOfMqm1hfV
— Naoki Aoyama (@AoiroAoino) 2017年7月29日
美味しかったです! pic.twitter.com/jdKzzUVULA
— Naoki Aoyama (@AoiroAoino) 2017年7月28日
これがラーソーメンっ...!? pic.twitter.com/UHO8JTJBP8
— Naoki Aoyama (@AoiroAoino) 2017年7月29日
きました pic.twitter.com/fRK5sPrz6A
— Naoki Aoyama (@AoiroAoino) 2017年7月30日
初出社に成功しました pic.twitter.com/GZR5aK4IXx
— Naoki Aoyama (@AoiroAoino) 2017年8月1日
— Naoki Aoyama (@AoiroAoino) 2017年8月1日
んにゃ pic.twitter.com/83Elv3pLat
— Naoki Aoyama (@AoiroAoino) 2017年8月3日
ようす pic.twitter.com/sDMUNf9e7O
— Naoki Aoyama (@AoiroAoino) 2017年8月21日
花金はこれからだ pic.twitter.com/bMdAh3ieMM
— Naoki Aoyama (@AoiroAoino) 2017年8月25日
旅行と飯と酒と Switch しかありません。進捗はどこに行ったのでしょうか。
退職します
ちょっと数日たってしまいましたが、日記です。
どうでもいい話ですが、6月からむしょくになります!
— Naoki Aoyama (@AoiroAoino) 2017年4月18日
いよいよ見納めなので記念に pic.twitter.com/zMLlbVimPD
— Naoki Aoyama (@AoiroAoino) 2017年5月2日
というわけで最終出社日でした
— Naoki Aoyama (@AoiroAoino) 2017年5月2日
GW の長さなら負けない自信があります!!!
— Naoki Aoyama (@AoiroAoino) 2017年5月2日
マーベリック株式会社を退職します。 5/2 が最終出社日で、5/31 が退職日です。
例のアレです↓ http://www.amazon.co.jp/registry/wishlist/3C4T2LY4FGFR6/ref=cm_sw_r_tw_ws_x_VJddzb0XGN15T
何してる人?
猫が大好きな、東京都内で Scala 書くお仕事してる人です。 Lens の話してたり Monocle ってライブラリのコミッターだったりする人です。 あと、猫が大好きです。
振り返って
振り返ってみて、自由な社風でとてもいい会社だったなと思ってます。 自分がコミッターになったのも、思い返してみれば OSS 活動に対する理解があった故でしたし。 直近ではとあるシステムの要件定義から始まり、構成考えてミドルウェアの検証したのち、 実装から各種テスト、デプロイ体制の整備あたりまで一通り担当できてとても自信に繋がりました。 開発/運用、アプリ/インフラなどなどにおける垣根や柵が(いい意味で)ほぼ存在してないので、 入社してから約二年半という短い期間で様々なことに取り組めましたしとても素晴らしい経験をすることが出来ました。
では何故?って話ですが、自分が数年間隔で考えてる将来の展望と比較して所謂音楽性の違い(?)みたいなのがあったのと、 長期的な観点で考えた時に環境を変えて新たな業種での開発もしてみたいと思ったのが理由です。
前向きなので特別なネタは無いですが、そのあたり聞きたい方は肉or寿司の場で(ぇ
まとめとか
とりあえず5月一杯の有給消化期間を全力でまったりと(?)進捗を出していこうかなと思っています。 次どこで何をするかは諸々確定してから改めて書く予定です。
お世話になった皆さん、本当にありがとうございました。 今後とも引き続きよろしくお願い致します。