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