88

このオプションを指定して Haskell アプリケーションをコンパイルすると-Wall、GHC は孤立したインスタンスについて不平を言います。たとえば、次のようになります。

Publisher.hs:45:9:
    Warning: orphan instance: instance ToSElem Result

型クラスは私のものではなく、 HStringTemplateToSElemによって定義されています。

これで、これを修正する方法 (インスタンス宣言を Result が宣言されているモジュールに移動する) がわかり、GHC が孤立したインスタンスを避けることを好む理由もわかりましたが、それでも私の方法の方が優れていると思います。コンパイラが不便かどうかは気にしません - 私ではなく。

インスタンスを Publisher モジュールで宣言する理由ToSElemは、他のモジュールではなく HStringTemplate に依存するのは Publisher モジュールであるためです。関心の分離を維持し、すべてのモジュールが HStringTemplate に依存することを回避しようとしています。

Haskell の型クラスの利点の 1 つは、たとえば Java のインターフェイスと比較した場合、閉じているのではなく開いているため、インスタンスをデータ型と同じ場所で宣言する必要がないことです。GHC のアドバイスは、これを無視するようです。

したがって、私が探しているのは、私の考えが健全であり、この警告を無視/抑制することが正当化されるという検証、または私のやり方に対するより説得力のある議論のいずれかです.

4

6 に答える 6

98

あなたがこれをやりたい理由は理解できますが、残念ながら、Haskell クラスがあなたの言うように「オープン」に見えるのは幻想に過ぎないかもしれません。多くの人は、これを行う可能性は Haskell 仕様のバグであると感じています。その理由については、以下で説明します。とにかく、クラスが宣言されているモジュールまたは型が宣言されているモジュールのいずれかで宣言する必要があるインスタンスに本当に適切でない場合、それはおそらくnewtypeまたは他のラッパーを使用する必要があるという兆候ですあなたのタイプの周り。

孤立したインスタンスを回避する必要がある理由は、コンパイラの利便性よりもはるかに深いところにあります。他の回答からわかるように、このトピックはかなり物議を醸しています。議論のバランスをとるために、孤立したインスタンスを決して書くべきではないという観点を説明します. 私自身の意見はその中間にあるので、最後に説明します。

この問題は、同じクラスと型に対して複数のインスタンス宣言が存在する場合、標準の Haskell にはどれを使用するかを指定するメカニズムがないという事実から生じます。むしろ、プログラムはコンパイラによって拒否されます。

その最も単純な効果は、モジュールの遠く離れた依存関係で他の誰かが行った変更のために突然コンパイルを停止する、完全に機能するプログラムを作成できることです。

さらに悪いことに、遠く離れた変更が原因で、動作中のプログラムが実行時にクラッシュし始める可能性があります。特定のインスタンス宣言から来ていると想定しているメソッドを使用している可能性があり、プログラムが不可解なクラッシュを開始するのに十分なだけ異なる別のインスタンスに静かに置き換えられる可能性があります。

これらの問題が決して起こらないという保証が必要な人は、誰かがどこかで特定の型の特定のクラスのインスタンスを宣言したことがある場合、作成されたプログラムで他のインスタンスを再度宣言してはならないという規則に従う必要があります。誰でも。もちろん、 a を使用しnewtypeて新しいインスタンスを宣言するという回避策もありますが、これは常に少なくとも小さな不便であり、時には重大な問題です。この意味で、故意に孤立したインスタンスを書く人はかなり失礼です。

では、この問題に対して何をすべきでしょうか?孤立インスタンス反対陣営は、GHC 警告はバグであり、孤立インスタンスを宣言する試みを拒否するエラーである必要があると述べています。それまでの間、私たちは自制心を働かせ、どんな犠牲を払ってもそれらを避けなければなりません。

見てきたように、潜在的な問題についてそれほど心配していない人もいます。あなたが提案するように、彼らは実際に孤立したインスタンスを懸念を分離するためのツールとして使用することを奨励しており、問題がないことをケースバイケースで確認する必要があると言っています。私は、他の人々の孤児の例に何度も不便を感じてきたので、この態度は無頓着すぎると確信しました。

インスタンスのインポートを制御する拡張機能を Haskell のインポート メカニズムに追加するのが正しい解決策だと思います。これで問題が完全に解決するわけではありませんが、世界にすでに存在する孤立したインスタンスからのダメージからプログラムを保護するのにいくらか役立ちます。そして、時間が経つにつれて、特定の限られたケースでは、おそらく孤立したインスタンスはそれほど悪くないかもしれないと確信するかもしれません. (そしてまさにその誘惑こそが、孤児院反対陣営の何人かが私の提案に反対している理由です。)

これらすべてからの私の結論は、少なくとも当面の間、他の理由がなければ他の人に配慮するために、孤立したインスタンスを宣言しないことを強くお勧めします。を使用しnewtypeます。

于 2010-06-20T15:22:01.103 に答える
45

この警告を抑制してください。

あなたは良い仲間です。Conalは「TypeCompose」でそれを行います。「chp-mtl」や「chp-transformers」、「control-monad-exception-mtl」や「control-monad-exception-monadsfd」などがそうです。

ところで、あなたはおそらくこれをすでに知っていますが、そうではなく、検索で質問につまずく人のために:

{-# OPTIONS_GHC -fno-warn-orphans #-}

編集:

私は、Yitz が彼の回答で言及した問題を実際の問題として認識しています。ただし、孤立したインスタンスを問題として使用していないこともわかり、孤立したインスタンスを慎重に使用するのに役立つ「すべての悪の中で最も少ない」ものを選択しようとしています。

あなたの質問は、あなたがすでに問題を十分に認識していることを示しているため、短い回答で感嘆符のみを使用しました。そうでなければ、私はそれほど熱心ではなかっただろう:)

少し気晴らしになりますが、私が信じているのは、妥協のない完璧な世界での完璧なソリューションです。

Yitz が言及している問題 (どのインスタンスが選択されているかわからない) は、次のような「全体論的」プログラミング システムで解決できると思います。

  • 原始的に単なるテキスト ファイルを編集するのではなく、むしろ環境によって支援されます (たとえば、コード補完は関連するタイプのもののみを提案するなど)。
  • 「下位レベル」言語には型クラスに対する特別なサポートはなく、代わりに関数テーブルが明示的に渡されます
  • しかし、「より高いレベル」のプログラミング環境は、Haskell が現在提示されている方法と同様の方法でコードを表示し (通常、渡された関数テーブルは表示されません)、明示的な型クラスが明らかな場合はそれらを選択します (たとえば、Functor のすべてのケースには 1 つの選択肢しかありません) いくつかの例 (リスト Applicative または list-monad Applicative、First/Last/lift の場合は Monoid) がいくつかある場合は、使用するインスタンスを選択できます。
  • いずれにせよ、インスタンスが自動的に選択された場合でも、環境では、簡単なインターフェイス (ハイパーリンクまたはホバー インターフェイスなど) を使用して、どのインスタンスが使用されたかを簡単に確認できます。

ファンタジーの世界 (またはできれば未来) から戻って、今: 孤立したインスタンスを「本当に必要な」ときに使用しながら、孤立したインスタンスを回避することをお勧めします。

于 2010-06-20T14:31:29.137 に答える
37

孤立したインスタンスは厄介ですが、私の意見では、それらが必要になる場合があります。タイプが1つのライブラリに由来し、クラスが別のライブラリに由来するライブラリを組み合わせることがよくあります。もちろん、これらのライブラリの作成者は、考えられるすべてのタイプとクラスの組み合わせのインスタンスを提供することを期待することはできません。だから私はそれらを提供しなければなりません、そしてそれで彼らは孤児です。

インスタンスを提供する必要があるときに型を新しい型でラップする必要があるという考えは、理論的にはメリットのある考えですが、多くの状況では面倒です。これは、Haskellのコードを生計のために書かない人々によって提唱された種類のアイデアです。:)

したがって、先に進み、孤立したインスタンスを提供します。彼らは無害です。
孤立したインスタンスでghcをクラッシュさせることができる場合、それはバグであり、そのように報告する必要があります。(ghcが複数のインスタンスを検出しないことに関して持っていた/持っていたバグは修正するのがそれほど難しくありません。)

ただし、将来、他の誰かが既に持っているインスタンスを追加する可能性があり、(コンパイル時)エラーが発生する可能性があることに注意してください。

于 2010-06-21T01:53:26.263 に答える
18

この場合、孤立したインスタンスの使用は問題ないと思います。私にとっての一般的な経験則は、型クラスを「所有」している場合、またはデータ型 (またはそのコンポーネント) を「所有」している場合、インスタンスを定義できます。つまり、Maybe MyData のインスタンスも問題ありません。少なくとも時々)。これらの制約内で、インスタンスをどこに配置するかはあなた次第です。

もう 1 つ例外があります。型クラスもデータ型も所有していないが、ライブラリではなくバイナリを生成している場合、それも問題ありません。

于 2010-06-20T23:43:58.963 に答える
4

これらの線に沿って、私はアンチオーファンインスタンスキャンプの位置WRTライブラリを理解していますが、実行可能ターゲットの場合、オーファンインスタンスは問題ないはずですか?

于 2010-06-22T10:58:48.433 に答える