1. ホーム

[解決済み】Template Haskellの何がそんなに悪いのか?

2022-04-04 13:02:09

質問

Haskellコミュニティでは、Template Haskellは不幸な利便性と捉えられることが多いようです。この点に関して私が観察したことを正確に言葉にするのは難しいのですが、以下のいくつかの例を考えてみてください。

Template Haskellを使って、通常のHaskellでは不可能なきれいな構文を可能にしたり、定型文を大幅に減らしたりしているブログ記事をいろいろと見てきました。では、なぜTemplate Haskellはこのように見下されているのでしょうか?何が好ましくないのでしょうか?どのような状況でTemplate Haskellを避けるべきなのか、またその理由は?

どうすれば解決するの?

Template Haskellを避ける理由の1つは、全体としてタイプセーフでないことです。

  • THコードの一部がどのようなHaskell ASTを生成するかは、それがどこに表示されるか以上にコントロールすることはできません。 Exp を表す式であるかどうかはわからない。 [Char] または (a -> (forall b . b -> c)) といった具合に。関数が特定の型の式だけを生成する、関数宣言だけを生成する、データコンストラクタにマッチするパターンだけを生成する、などと表現できれば、THはより信頼できるものになるでしょう。
  • コンパイルできない式を生成することができます。自由変数を参照する式を生成しました。 foo 存在しない?運が悪いことに、実際にコード・ジェネレーターを使うときに、その特定のコードの生成のきっかけとなる状況下でしか、そのようなことは起こらないのです。また、ユニットテストも非常に困難です。

THはまた、まったくもって危険です。

  • コンパイル時に実行されるコードで、任意に IO ミサイルを撃ったり、クレジットカードを盗んだり。THエクスプロイトを探すために、ダウンロードしたすべてのCabalパッケージに目を通す必要はないでしょう。
  • モジュールのプライベートな関数や定義にアクセスすることができ、場合によってはカプセル化を完全に破壊することができます。

それから、ライブラリ開発者としてTH関数を使うのが楽しくなくなるような問題もあります。

  • THのコードは必ずしもコンポーザブルではない。例えば、誰かがレンズのジェネレータを作ったとします。そのジェネレータは、他のTHコードではなく、エンドユーザーが直接呼び出すことができるような構造になっていることが多く、例えば、レンズを生成するための型コンストラクタのリストをパラメータとして受け取ります。このリストをコードで生成するのはやっかいです。 generateLenses [''Foo, ''Bar] .
  • 開発者は 知っている THのコードが構成できること。を書くことができるのをご存知でしょうか? forM_ [''Foo, ''Bar] generateLens ? Q は単なるモナドなので、その上で通常の関数をすべて使用することができます。それを知らない人がいるから、同じ機能を持つ関数のオーバーロード版をいくつも作ってしまい、ある種の肥大化を招いてしまう。また、ほとんどの人はジェネレータを Q を書くようなものです。 bla :: IO Int; bla = return 3 関数に必要以上の環境("environment")を与えることになり、関数のクライアントはその効果としてその環境を提供することを要求されます。

最後に、THの関数をエンドユーザーとして使うのが楽しくなくなるようなことがあります。

  • 不透明度。TH関数に型がある場合 Q Dec というのも、モジュールのトップレベルにはどんなものでも生成できてしまい、何が生成されるかは全くコントロールできないからです。
  • モノリシック。開発者が許可しない限り、TH関数が生成する量をコントロールすることはできません。もし、データベース・インターフェースを生成する関数を見つけたとしたら JSONシリアライゼーション・インターフェースの場合、「いや、私はデータベース・インターフェースだけが欲しいんだ、ありがとう、JSONインターフェースは自分で作るよ」とは言えません。
  • 実行時間。THコードの実行には比較的長い時間がかかります。ファイルがコンパイルされるたびにコードが新たに解釈され、しばしば、実行中のTHコードが必要とする大量のパッケージがロードされなければなりません。このため、コンパイル時間がかなり遅くなります。