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

Perlでも関数の型をチェックしたい

36e748c47de34549cfa3ac0950c5351d?s=47 ybrliiu
February 18, 2021

 Perlでも関数の型をチェックしたい

36e748c47de34549cfa3ac0950c5351d?s=128

ybrliiu

February 18, 2021
Tweet

Transcript

  1. Perl でも関数の型をチェックしたい Japan.pm 2021 id: ybrliiu

  2. 自己紹介 • id: _ybrliiu / mp0liiu • 所属 : 株式会社モバイルファクトリー

    • 普段はPerl書いています • 最近は古くなったフロントエンドの エコシステムをアップデートする仕事をしています
  3. 突然ですが 次のコードを見てどう思いますか?

  4. None
  5. インスタンス変数の型に サブルーチンリファレンスの型を指定

  6. コールバックに渡される引数、 期待する返り値が何なのかわからん!

  7. 期待していないような値を返す コールバックが渡されたらどうしよう?

  8. コールバック関数に型がついてほしい

  9. このようになってほしい

  10. このようになってほしい コールバックに渡される引数、 期待する返り値が明示されている

  11. Perlにおける型制約事情 • 動的型付け言語なので型チェックは動的に行われる • 外部にインターフェースを公開する部分はしばしば型チェックが行われて いる ◦ インスタンス変数や関数の引数の型など ◦ 型制約を設けることによるメリットが多い

  12. 具体例 1. クラスビルダーによる型チェック 2. 引数バリデーターによる型チェック 3. 型制約ライブラリ

  13. クラスビルダーによる型チェック • クラスビルダー ◦ オブジェクト指向関連の実装を簡単に記述できるライブラリ ◦ e.g.) C::A::L, Moose, Mouse,

    Dios, Zydeco, etc... • 組み込みで型制約システムを持っているクラスビルダーがある • アトリビュートを作るとインスタンス変数の型をチェックしてくれる
  14. Mooseによるインスタンス変数の型チェックの例

  15. 引数バリデーターによる型チェック • CPAN には引数をチェックする様々なモジュールがある ◦ e.g.) Params::Validate, Data::Validator, Smart::Args, Type::Params,

    Function::Parameters etc... • クラスビルダー組み込みの型制約システムや、型制約ライブラリと組 み合わせて使うことで引数の型をチェックできる
  16. Smart::Args による引数の型チェックの例

  17. 型制約ライブラリ • 型チェックする機能だけを提供するモジュール ◦ e.g.) Type::Tiny, Specio • 再利用性が高い •

    最近は Type::Tiny とクラスビルダーや引数バリデーターと組み合わ せて使うことがトレンド • 同梱の Types::Standard で提供されている型でほとんどのユース ケースをカバーできる
  18. Type::Tiny による型チェックの例

  19. Perlの関数型の現状 • どの型制約ライブラリやクラスビルダーを探しても、コードリファレンス であるかをチェックする CodeRef 型しか存在しない • 関数の引数の型や返り値の型をチェック / 明示したくてもできない

    ◦ 他の型をしっかり書いていても関数の部分だけ割れ窓になる
  20. ないなら実装するぞ!!!

  21. 実装するにあたって必要なもの • 関数の型情報を付与 / 取得できる仕組み • 2つの関数の型情報を比較する仕組み • 関数の型情報を比較する型

  22. 関数の型情報を付与 / 取得できる仕組みへの要求 • CodeRef の引数の型と返り値の型情報を付与し、それらをチェックす る • 後から引数の型と返り値の型情報を取得できるようにする •

    引数の型と返り値の型情報は CodeRef のスコープが外れたら破棄 して欲しい • 実行時のオーバーヘッドをなるべく減らしたい
  23. Sub::WrapInType の実装 • 前述の要求を満たすものがなかったので実装 • wrap_sub 関数に CodeRef、引数の型、返り値の型を与えると、与え られた CodeRef

    を引数の型と返り値の型をチェックする処理でラップ したCodeRefを返す
  24. Sub::WrapInType の利用例

  25. • wrap_sub で生成される CodeRef は bless されたオブジェクト ◦ 関数の型の情報はスコープが外れたら破棄される ◦

    CodeRef をそのまま実行できる ▪ ハッシュベースのクラスで演算子オーバーロードする場合と比 べてコード実行時のオーバーヘッドが少ない • Inside-out object というテクニックでクラスを実装 Sub::WrapInType の実装
  26. 実装するにあたって必要なもの • 関数の型情報を付与 / 取得できる仕組み • 2つの関数の型情報を比較する仕組み • 関数の型情報を比較する型

  27. 2つの関数の型情報を比較する仕組み Sub::Meta を利用する

  28. 実装するにあたって必要なもの • 関数の型情報を付与 / 取得できる仕組み • 2つの関数の型情報を比較する仕組み • 関数の型情報を比較する型

  29. 関数の型情報を比較する型の実装 • Sub::WrapInType で関数の型の情報を付与した CodeRef を Sub::Meta で比較する型を実装する • ポータビリティを考慮し

    Type::Tiny で型を実装する ◦ 様々なクラスビルダーや引数バリデーターと組み合わせて利用で きる
  30. Type::Tiny での独自型の実装 • Type::Tiny のコンストラクタに値をチェックする処理、親にあたる型、 型名、型強制する場合の処理などを渡して実装する ◦ Type::Utils のユーティリティ関数を用いれば簡単 •

    型オブジェクトを返す関数を作ってエクスポートする ◦ Type::Library を利用すると簡単
  31. Type::Tiny での独自型の実装例

  32. 総称型 • 関数の型情報を比較する型は関数の型情報をパラメータにとる総称 型になる • 総称型とは ◦ 抽象化された型で、型パラメータを渡すと具体化する ◦ Type::Tiny

    でいう ArrayRef[T], HashRef[T] など
  33. Type::Tiny での総称型の実装 • 独自の総称型を簡単に実装できるユーティリティは存在しない • 型パラメータが渡されたとき型名、型制約、型強制がどのように具体化 されるかを定義する ◦ name_generator, constraint_generator,

    coercion_generator • 上記のパラメータを Type::Tiny に渡してインスタンスを生成 • 型パラメータリストを ArrayRef で取り型を具体化する関数を作る
  34. Type::Tiny での総称型の実装例

  35. Type::Tiny での総称型の実装例

  36. Type::Tiny での総称型の実装例

  37. 実装するにあたって必要なもの • 関数の型情報を付与 / 取得できる仕組み • 2つの関数の型情報を比較する仕組み • 関数の型情報を比較する型

  38. 関数の型をチェックする型 Types::TypedCodeRef が完成!

  39. Perlで関数の型チェックを実現

  40. この型を使って最初のコードを書き換え てみましょう

  41. None
  42. どんなコールバック関数を渡せばいいかひ と目でわかる!!!

  43. 型強制もできる

  44. 型強制もできる CodeRef を渡すと Types::TypedCodeRef の型情報を使い Sub::WrapInType でラップしてくれる

  45. DEMO

  46. 今後の展望 • inline 化してパフォーマンス良くしたい ◦ 型チェックするコードを文字列化して結合し eval することで、関数 呼び出しのオーバーヘッドがなくなる •

    エラーメッセージをわかりやすくしたい ◦ 現状急に「型チェックに失敗した!」みたいなエラーがでてくるの で何が原因でエラーになったのかわかりにくい
  47. まとめ

  48. Types::TypedCodeRef で コールバックの型をつけよう!

  49. Any Questions?