Perlでも関数の型をチェックしたい
by
ybrliiu
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Perl でも関数の型をチェックしたい Japan.pm 2021 id: ybrliiu
Slide 2
Slide 2 text
自己紹介 ● id: _ybrliiu / mp0liiu ● 所属 : 株式会社モバイルファクトリー ● 普段はPerl書いています ● 最近は古くなったフロントエンドの エコシステムをアップデートする仕事をしています
Slide 3
Slide 3 text
突然ですが 次のコードを見てどう思いますか?
Slide 4
Slide 4 text
No content
Slide 5
Slide 5 text
インスタンス変数の型に サブルーチンリファレンスの型を指定
Slide 6
Slide 6 text
コールバックに渡される引数、 期待する返り値が何なのかわからん!
Slide 7
Slide 7 text
期待していないような値を返す コールバックが渡されたらどうしよう?
Slide 8
Slide 8 text
コールバック関数に型がついてほしい
Slide 9
Slide 9 text
このようになってほしい
Slide 10
Slide 10 text
このようになってほしい コールバックに渡される引数、 期待する返り値が明示されている
Slide 11
Slide 11 text
Perlにおける型制約事情 ● 動的型付け言語なので型チェックは動的に行われる ● 外部にインターフェースを公開する部分はしばしば型チェックが行われて いる ○ インスタンス変数や関数の引数の型など ○ 型制約を設けることによるメリットが多い
Slide 12
Slide 12 text
具体例 1. クラスビルダーによる型チェック 2. 引数バリデーターによる型チェック 3. 型制約ライブラリ
Slide 13
Slide 13 text
クラスビルダーによる型チェック ● クラスビルダー ○ オブジェクト指向関連の実装を簡単に記述できるライブラリ ○ e.g.) C::A::L, Moose, Mouse, Dios, Zydeco, etc... ● 組み込みで型制約システムを持っているクラスビルダーがある ● アトリビュートを作るとインスタンス変数の型をチェックしてくれる
Slide 14
Slide 14 text
Mooseによるインスタンス変数の型チェックの例
Slide 15
Slide 15 text
引数バリデーターによる型チェック ● CPAN には引数をチェックする様々なモジュールがある ○ e.g.) Params::Validate, Data::Validator, Smart::Args, Type::Params, Function::Parameters etc... ● クラスビルダー組み込みの型制約システムや、型制約ライブラリと組 み合わせて使うことで引数の型をチェックできる
Slide 16
Slide 16 text
Smart::Args による引数の型チェックの例
Slide 17
Slide 17 text
型制約ライブラリ ● 型チェックする機能だけを提供するモジュール ○ e.g.) Type::Tiny, Specio ● 再利用性が高い ● 最近は Type::Tiny とクラスビルダーや引数バリデーターと組み合わ せて使うことがトレンド ● 同梱の Types::Standard で提供されている型でほとんどのユース ケースをカバーできる
Slide 18
Slide 18 text
Type::Tiny による型チェックの例
Slide 19
Slide 19 text
Perlの関数型の現状 ● どの型制約ライブラリやクラスビルダーを探しても、コードリファレンス であるかをチェックする CodeRef 型しか存在しない ● 関数の引数の型や返り値の型をチェック / 明示したくてもできない ○ 他の型をしっかり書いていても関数の部分だけ割れ窓になる
Slide 20
Slide 20 text
ないなら実装するぞ!!!
Slide 21
Slide 21 text
実装するにあたって必要なもの ● 関数の型情報を付与 / 取得できる仕組み ● 2つの関数の型情報を比較する仕組み ● 関数の型情報を比較する型
Slide 22
Slide 22 text
関数の型情報を付与 / 取得できる仕組みへの要求 ● CodeRef の引数の型と返り値の型情報を付与し、それらをチェックす る ● 後から引数の型と返り値の型情報を取得できるようにする ● 引数の型と返り値の型情報は CodeRef のスコープが外れたら破棄 して欲しい ● 実行時のオーバーヘッドをなるべく減らしたい
Slide 23
Slide 23 text
Sub::WrapInType の実装 ● 前述の要求を満たすものがなかったので実装 ● wrap_sub 関数に CodeRef、引数の型、返り値の型を与えると、与え られた CodeRef を引数の型と返り値の型をチェックする処理でラップ したCodeRefを返す
Slide 24
Slide 24 text
Sub::WrapInType の利用例
Slide 25
Slide 25 text
● wrap_sub で生成される CodeRef は bless されたオブジェクト ○ 関数の型の情報はスコープが外れたら破棄される ○ CodeRef をそのまま実行できる ■ ハッシュベースのクラスで演算子オーバーロードする場合と比 べてコード実行時のオーバーヘッドが少ない ● Inside-out object というテクニックでクラスを実装 Sub::WrapInType の実装
Slide 26
Slide 26 text
実装するにあたって必要なもの ● 関数の型情報を付与 / 取得できる仕組み ● 2つの関数の型情報を比較する仕組み ● 関数の型情報を比較する型
Slide 27
Slide 27 text
2つの関数の型情報を比較する仕組み Sub::Meta を利用する
Slide 28
Slide 28 text
実装するにあたって必要なもの ● 関数の型情報を付与 / 取得できる仕組み ● 2つの関数の型情報を比較する仕組み ● 関数の型情報を比較する型
Slide 29
Slide 29 text
関数の型情報を比較する型の実装 ● Sub::WrapInType で関数の型の情報を付与した CodeRef を Sub::Meta で比較する型を実装する ● ポータビリティを考慮し Type::Tiny で型を実装する ○ 様々なクラスビルダーや引数バリデーターと組み合わせて利用で きる
Slide 30
Slide 30 text
Type::Tiny での独自型の実装 ● Type::Tiny のコンストラクタに値をチェックする処理、親にあたる型、 型名、型強制する場合の処理などを渡して実装する ○ Type::Utils のユーティリティ関数を用いれば簡単 ● 型オブジェクトを返す関数を作ってエクスポートする ○ Type::Library を利用すると簡単
Slide 31
Slide 31 text
Type::Tiny での独自型の実装例
Slide 32
Slide 32 text
総称型 ● 関数の型情報を比較する型は関数の型情報をパラメータにとる総称 型になる ● 総称型とは ○ 抽象化された型で、型パラメータを渡すと具体化する ○ Type::Tiny でいう ArrayRef[T], HashRef[T] など
Slide 33
Slide 33 text
Type::Tiny での総称型の実装 ● 独自の総称型を簡単に実装できるユーティリティは存在しない ● 型パラメータが渡されたとき型名、型制約、型強制がどのように具体化 されるかを定義する ○ name_generator, constraint_generator, coercion_generator ● 上記のパラメータを Type::Tiny に渡してインスタンスを生成 ● 型パラメータリストを ArrayRef で取り型を具体化する関数を作る
Slide 34
Slide 34 text
Type::Tiny での総称型の実装例
Slide 35
Slide 35 text
Type::Tiny での総称型の実装例
Slide 36
Slide 36 text
Type::Tiny での総称型の実装例
Slide 37
Slide 37 text
実装するにあたって必要なもの ● 関数の型情報を付与 / 取得できる仕組み ● 2つの関数の型情報を比較する仕組み ● 関数の型情報を比較する型
Slide 38
Slide 38 text
関数の型をチェックする型 Types::TypedCodeRef が完成!
Slide 39
Slide 39 text
Perlで関数の型チェックを実現
Slide 40
Slide 40 text
この型を使って最初のコードを書き換え てみましょう
Slide 41
Slide 41 text
No content
Slide 42
Slide 42 text
どんなコールバック関数を渡せばいいかひ と目でわかる!!!
Slide 43
Slide 43 text
型強制もできる
Slide 44
Slide 44 text
型強制もできる CodeRef を渡すと Types::TypedCodeRef の型情報を使い Sub::WrapInType でラップしてくれる
Slide 45
Slide 45 text
DEMO
Slide 46
Slide 46 text
今後の展望 ● inline 化してパフォーマンス良くしたい ○ 型チェックするコードを文字列化して結合し eval することで、関数 呼び出しのオーバーヘッドがなくなる ● エラーメッセージをわかりやすくしたい ○ 現状急に「型チェックに失敗した!」みたいなエラーがでてくるの で何が原因でエラーになったのかわかりにくい
Slide 47
Slide 47 text
まとめ
Slide 48
Slide 48 text
Types::TypedCodeRef で コールバックの型をつけよう!
Slide 49
Slide 49 text
Any Questions?