Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dotty で軽量な DI ライブラリをかいてみた

Dotty で軽量な DI ライブラリをかいてみた

Avatar for Jun Tomioka

Jun Tomioka

August 26, 2020
Tweet

More Decks by Jun Tomioka

Other Decks in Technology

Transcript

  1. Dotty で軽量な DI ライブラリを書いてみた • つくったもの紹介 • テクニック ◦ Tuple

    ◦ given • その他 Dotty の機能 • まとめと感想 ※今回試したバージョンは 0.26.0-RC1
  2. • Design.of[Try] で Try を文脈として扱うことを宣言。このコンテナ作成は 失敗する可能性がある (Success | Failure) ◦

    利用者がこの文脈を選べる (Future, Resource 等)。 ◦ Tagless Final スタイル • bind は コンテナに登録するインスタンスのファクトリを指定。ここでは単 に Foo, Bar のapply を指定。 ◦ それぞれ Int => Foo, Foo => Bar が登録されている。 • bindF は Try[Baz] を返すファクトリを登録。渡された Int が負数の場合 に失敗。
  3. 特徴 • 機能 ◦ コンパイル時に依存の充足をチェック ▪ 必要な依存が未登録の場合にコンパイルエラー ◦ 任意の文脈 (Try,

    Future 等) で実行可能 ▪ 非同期処理を挟んだり、リソース管理したりできる • コード ◦ core 部分 100行未満のDIコンテナ ▪ (一般的な型クラス・ADTの自前実装を除く) ◦ 依存は標準ライブラリのみ ◦ マクロ未使用 ▪ compiletime package も未使用
  4. Tuple • Dotty の Tuple はジェネリック・型レベルのプログ ラミングを強くサポート ◦ shapeless の

    HList のように柔軟に扱える ▪ 例) (Int, String) =:= Int *: String *: EmptyTuple ▪ interface や構造は List を想像するとわかりやすい。 ◦ 型レベル の Map や Fold, FlatMap まである https://github.com/lampepfl/dotty/blob/0.26.0-RC1/library/src/scala/Tuple.scala
  5. Tuple (実践) • 今回の Design 型 は bind された factory

    (Entry) をすべて Tuple として 型パラメータEntries に追 加していく ◦ イメージ: Design[Entry2 *: Entry1 *: EmptyTuple] • bind された様々な型を持ち運ぶことができる。(これ により依存の充足をコンパイル時に判断できる)
  6. given / using • Dotty 版の implicit parameter (誤解を恐れずに言えば )

    ◦ implicit な値, Function の宣言に given ◦ 引数として implicit な値を利用する際に using ◦ いくつか細かい機能に変更がある ▪ import 時に通常の値と区別 ▪ Not による優先順位の指定 ▪ ... • Scala 2 の implicit で使えた再帰的な検索のテク ニックも健在 ◦ 今回多用している
  7. given / using (再帰的な検索) • using を 持った given ◦

    using なものが見つかった場合に given が成り立つ。 ◦ Scala 2 の implicit def x(implicit y: Y): X = ??? というパ ターン
  8. given / using (実践) • 依存グラフの作成に再帰的な given のサーチを 利用 •

    Resolve[Out <: Tuple] は Design に登録されている Entries から Out 型を 生成できる apply メソッドをもつ ◦ Resolved[Out] ≒ Out だと思ってもらって良い • このインスタンスが存在するということは、つまり、 Design は Out 型を生成 するのに十分なファクトリが登録されている ということ。
  9. given / using (実践) • 依存グラフの作成に再帰的な given のサーチを 利用 •

    given … as Resolve[EmptyTuple] は using なしで宣言されている。 ◦ つまり、いずれにしても EmptyTuple を生成することは可能 (それはそう)
  10. given / using (実践) • 依存グラフの作成に再帰的な given のサーチを 利用 •

    given … as Resolve[Head *: Tail] は using が宣言された上で定義されている。 ◦ つまり、以下 using の条件を満たす場合に Head *: Tail は生成できる 1. Head の型を返却する Entry が Entries の中に存在する (FindEntry[Entries, Head]) 2. 2. の Entry が依存する引数リストが生成できる (findEntry.In) • この引数リストもまた Tuple なので、これを再帰的に検索 • 引数なしのものは EmptyTuple を引数として登録しており、そこが再帰の終了条件とな る 3. Tail が生成できる (Resolve[Tail]) • Dotty は標準ライブラリで Tuple が generic に扱えるようになったのでこのような再帰的なテクニックが活 用しやすい
  11. given / using (実践) • Design の resolveAll メソッドは Resolve[Outputs[Entries]]

    型 が given される場 合に利用できる。 ◦ つまり、すべての依存を満たす場合にコンパイ ルでき、そうでない場合にコンパイルエラー
  12. その他 Dotty の機能 (感想) • Optional Braces ◦ {} のかわりに

    yaml っぽい感じにインデントでブロックを記 述できる ◦ 議論されているときはウッと思ったけど意外に良いかも。 (縦に短くなる) • Match types ◦ 型のパターンマッチ ◦ 超強力だが今回はあまり使っていない ▪ Entry から出力型をとるのに使っている ◦ ゴリゴリに使った例はこちら (宣伝) ▪ Scala のコンパイラに FizzBuzz を解いてもらう (Dotty もあるよ)
  13. その他 Dotty の機能 (感想) • Extension Methods ◦ よくある拡張メソッド ◦

    記述は簡潔だが、implicit class にできたことができなく なってしまっている
  14. その他 Dotty の機能 (ハマったところ) • dependent function type を 保持して扱おうとし

    たが上手くいかなかった ◦ 目的の Out を持つ Entry を Head から順に検索する findEntry trait ◦ 下記 findEntryInTail は type In = findEntry.In とできそう なきがしたが、この In が後々上手く解決できない ◦ 結局 Aux パターンを使った
  15. その他 Dotty の機能 (ハマったところ) • 再帰的な given の検索がなんか上手くいかない ケースがある(致命的) ◦

    given のサーチパスがバグっているようなきがする ▪ 再帰したときに companion object の given を読んでくれないケースがあ る ◦ Design class 内に必要な given を置いたパターンだとな ぜか上手くいくのでいったんそうしている ▪ DI の 呼び出し方に癖があるので納得いっていない ▪ Issue あげるかも
  16. まとめ • Dotty で軽量な DI ライブラリをかいてみた • 標準ライブラリの Tuple がいい感じに扱えるので

    手軽に型レベル・ジェネリックに凝ったことができ る • Dotty 今後に期待