この答えは、illissiusによってポイントごとに持ち上がった問題への応答です。
- 使うのは醜いです。$(fooBar '' Asdf)は見栄えがしません。表面的な、確かですが、それは貢献しています。
同意する。$()は、言語の一部のように見えるように選択されたように感じます-Haskellの使い慣れたシンボルパレットを使用しています。しかし、それはまさにあなたがマクロスプライシングに使用されるシンボルにあなたが望まない/望まないことです。それらは間違いなく配合量が多すぎますが、この化粧品の側面は非常に重要です。{{}}の外観は、スプライスが非常に視覚的に区別できるため、気に入っています。
- 書くのはもっと醜いです。引用は時々機能しますが、多くの場合、手動でASTのグラフトと配管を行わなければなりません。[API] [1]は大きく扱いにくいため、気にしなくてもディスパッチする必要があるケースは常に多くあります。気になるケースは、似ているが同一ではない形式で複数存在する傾向があります(データvs. newtype、record-style vs.通常のコンストラクターなど)。書くのは退屈で繰り返しが多く、機械的ではないほど複雑です。[改革案] [2]はこれの一部に対処します(引用をより広く適用できるようにします)。
私もこれに同意しますが、「THの新しい方向性」のコメントの一部に見られるように、すぐに使えるAST引用の欠如は重大な欠陥ではありません。このWIPパッケージでは、これらの問題にライブラリ形式で対処しようとしています。https://github.com/mgsloan/quasi-extras。これまでのところ、スプライシングを通常より数カ所多く許可しており、ASTでパターンマッチングを行うことができます。
- ステージ制限は地獄です。同じモジュールで定義された関数をスプライスできないのは、その小さな部分です。別の結果として、トップレベルのスプライスがある場合、モジュール内のそれ以降のすべては、その前の何にもスコープから外れます。このプロパティを持つ他の言語(C、C ++)は、宣言を転送できるようにすることで機能しますが、Haskellはそうではありません。スプライスされた宣言またはそれらの依存関係と依存関係の間で循環参照が必要な場合は、通常、ただ混乱しています。
私は以前に不可能であった循環TH定義の問題に遭遇しました...それは非常に迷惑です。解決策はありますが、それは醜いです-生成されたすべての宣言を組み合わせるTH式で循環依存関係に関係するものをラップしてください。これらの宣言ジェネレーターの1つは、Haskellコードを受け入れる準クォーターにすぎません。
- それは無理です。つまり、抽象化を表現する場合、ほとんどの場合、その抽象化の背後にはある種の原則や概念があります。多くの抽象化では、それらの背後にある原則をそのタイプで表現できます。タイプクラスを定義すると、多くの場合、インスタンスが従うべき法則を定式化し、クライアントが引き受けることができます。GHCの[新しいジェネリック機能] [3]を使用して、任意のデータ型(境界内)でインスタンス宣言の形式を抽象化すると、「合計型の場合、このように機能し、製品型の場合、そのように機能します。 」しかし、テンプレートHaskellは単なるばかげたマクロです。それはアイデアのレベルでの抽象化ではなく、ASTのレベルでの抽象化です。プレーンテキストのレベルでの抽象化よりも優れていますが、適度です。
あなたがそれで無茶なことをするなら、それは無茶です。唯一の違いは、コンパイラーが実装した抽象化のメカニズムにより、抽象化に漏れがないという確信が高まることです。おそらく、言語デザインを民主化することは少し恐ろしいように聞こえます!THライブラリの作成者は、提供するツールの意味と結果を明確かつ明確に文書化する必要があります。原則的なTHの良い例は、派生パッケージです。http://hackage.haskell.org/package/derive-派生の多くの例が実際の派生を/ specify /するようにDSLを使用します。
- それはあなたをGHCに結びつけます。理論的には別のコンパイラーがそれを実装することもできますが、実際にはこれが起こることはないと思います。(これは、現時点でGHCによってのみ実装されている可能性があるさまざまな型システム拡張とは対照的ですが、将来、他のコンパイラーによって採用され、最終的に標準化されることは容易に想像できます。)
それはかなり良い点です-TH APIはかなり大きくて不格好です。再実装するのは難しいかもしれません。ただし、Haskell ASTを表す問題を切り分ける方法はほんのわずかです。TH ADTをコピーし、内部AST表現にコンバーターを作成すると、そこにかなりの道が得られると思います。これは、haskell-src-metaを作成する(重要ではない)作業と同等です。また、TH ASTをきれいに出力し、コンパイラーの内部パーサーを使用することで、単純に再実装することもできます。
私は間違っているかもしれませんが、実装の観点からは、THがコンパイラー拡張の複雑なものであるとは思いません。これは実際には「シンプルに保つ」ことの利点の1つであり、基本的なレイヤーが理論的に魅力的な、静的に検証可能なテンプレートシステムにならないことです。
- APIは安定していません。新しい言語機能がGHCに追加され、それらをサポートするようにtemplate-haskellパッケージが更新される場合、これには、THデータ型に対する下位互換性のない変更が含まれることがよくあります。THコードをGHCの2つ以上のバージョンと互換性を持たせたい場合は、非常に注意して、おそらく使用する必要があります
CPP
。
これも良い点ですが、やや劇的です。最近、APIの追加が行われていますが、破損を引き起こすような広範囲な変更は行われていません。また、前述の優れたASTクォーティングを使用すると、実際に使用する必要があるAPIを大幅に削減できると思います。構築/照合で別個の関数が必要なく、代わりにリテラルとして表現されている場合、ほとんどのAPIは表示されなくなります。さらに、作成したコードは、Haskellに類似した言語のAST表現に移植しやすくなります。
要約すると、THは強力な、半無視されたツールだと思います。嫌悪感が減れば、ライブラリのより活発なエコシステムにつながり、より多くの言語機能プロトタイプの実装を促進できます。THは強力なツールであり、ほとんどすべてのことを/ do /させることができることが観察されています。無政府状態!まあ、それは、このパワーを使用すると、その制限のほとんどを克服でき、非常に原理的なメタプログラミングアプローチが可能なシステムを構築できると私は考えています。このように「適切な」実装の設計が徐々に明らかになるため、「適切な」実装をシミュレートするために醜いハックを使用する価値はあります。
私の個人的な理想のバージョンのニルヴァーナでは、言語の多くは実際にはコンパイラーからこれらのさまざまなライブラリーに移動します。ライブラリとして機能が実装されているという事実は、忠実に抽象化する機能に大きな影響を与えません。
ボイラープレートコードに対する典型的なHaskellの答えは何ですか?抽象化。私たちのお気に入りの抽象化は何ですか?関数と型クラス!
型クラスを使用すると、一連のメソッドを定義できます。これにより、そのクラスで汎用的なすべての関数で使用できます。ただし、これ以外に、クラスがボイラープレートを回避する唯一の方法は、「デフォルトの定義」を提供することです。さて、ここに非公認機能の例があります!
THの拒否とlispのようなメタプログラミングは、インスタンスの宣言のようなより柔軟なマクロ展開の代わりに、method-defaultsのようなものへの選好につながったと思います。予期しない結果につながる可能性のあるものを回避する規律は賢明ですが、Haskellの有能な型システムが他の多くの環境よりも信頼できるメタプログラミングを可能にすることを無視してはなりません(生成されたコードをチェックすることにより)。