case classのフィールド名とフィールドに対応する値を渡すと型安全にフィールド名と対応する値の組を渡してくれるアノテーションをscalametaで書いた

ややこしいタイトルシリーズ(?)

モチベーションが伝わりづらいけどDBへのアップデートでフィールドを4つか5つ指定したい(かつcase classのインスタンスは情報が足りなくて作れないという制約がある)という状況を考えます。

このとき sql.update(テーブル, Map[更新するカラムの名前 -> 更新する値]) のようなインターフェースがあるとするとMap[フィールド名 => Any] のようなものが必要になります。 例えば User(id: Long, tpe: Int, name: String) では Map("id" -> 0L, tpe: 1, name: "モフたろう") のようなものになる。

フィールド名を手書きするのは嫌だし、idに間違えてStringを渡してしまうことも避けたいので (フィールド名, そのフィールドに応じた型) というタプルを型安全に作ってからMap[String, Any]を生成する方針にしたい。

ということで Mofu.MacroPorter.wan(value = 1) のようにすると型チェックされた上で “wan” -> 1 がかえってくるマクロを作りました。 コンパニオンオブジェクトにフィールド名と全く同じ名前のメソッドが生えます。(同じ名前で使いやすいのか微妙だ)

gist.github.com

感想

  • shapelessのLensを使えばフィールド名の取得は行けそうだったけど、渡されたフィールドの型に応じた型をチェックするのが難しかった。LabelledGenericもLensもインスタンスがないとフィールドの型チェックが難しそうにみえた。情報としては揃っていてcan not proveになやまされたのでテクニックを知っていれば多分取れそう。

  • 書いたけど例のごとくIntelliJでは真っ赤なので作ってみたけど微妙だなーとなったのでそっ閉じ。