1. ホーム
  2. haskell

[解決済み] Haskellにおける孤児化したインスタンス

2023-04-14 19:53:24

質問

Haskellアプリケーションを -Wall オプションでコンパイルすると、GHC は例えば孤立したインスタンスについて文句を言います。

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

型クラス ToSElem は私のものではなく、以下のように定義されています。 HStringTemplate .

これで、これを修正する方法(インスタンス宣言をResultが宣言されているモジュールに移動する)がわかり、さらに が孤児となったインスタンスを避けたい理由もわかりました。 しかし、私はまだ私の方法の方が優れていると信じています。 私はコンパイラが不便になることを気にしていません - むしろ私よりもコンパイラです。

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

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

ですから、私が探しているのは、私の考え方が健全で、この警告を無視したり抑えたりすることが正当であるという検証か、あるいは私の方法で物事を行うことに対するより説得力のある議論かのいずれかです。

どのように解決するのですか?

やりたい気持ちはわかりますが、残念ながらHaskellのクラスが仰るように"open"に見えるのは錯覚に過ぎないかもしれません。後述する理由から、このようなことができるのはHaskellの仕様のバグだと感じている人も少なくないようです。とにかく、もし本当に必要なインスタンスが、クラスが宣言されているモジュールか、型が宣言されているモジュールのどちらかで宣言されるのが適切でないなら、それはおそらく、あなたが newtype や他のラッパーを使うべきというサインでしょう。

孤児インスタンスを避ける必要がある理由は、コンパイラの利便性よりもはるかに深いところにあります。このトピックは、他の回答からわかるように、かなり議論の余地があります。議論のバランスをとるために、私は、経験豊富な Haskeller の間では多数意見であると思われる、オーファン インスタンスを決して、決して書くべきではないという観点を説明するつもりです。私自身の意見は中間のようなもので、最後に説明します。

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

その最も単純な効果は、完全に動作していたプログラムが、他の誰かがあなたのモジュールの依存関係の遠くで行った変更のために、突然コンパイルを停止してしまうということです。

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

このような問題が起こらないことを保証してほしい人は、「誰でも、どこでも、特定の型のために特定のクラスのインスタンスを宣言したことがあるなら、他のインスタンスは、誰によって書かれたどんなプログラムでも、二度と宣言してはならない」というルールに従わなければなりません。もちろん、回避策として newtype を使って新しいインスタンスを宣言するという回避策はありますが、これは常に少なくとも小さな不都合であり、時には大きな不都合になります。 ですから、この意味で、意図的にオーファンインスタンスを書く人は、むしろ無礼なことなのです。

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

お分かりのように、このような潜在的な問題をあまり心配していない人たちもいます。彼らは、あなたが言うように、懸念事項の分離のためのツールとして孤児インスタンスの使用を実際に奨励しており、問題がないことをケースバイケースで確認すればよいと言っています。私は、他人のオーファンインスタンスによって何度も迷惑を被っているので、この態度はあまりにも軽率だと確信しています。

私は、正しい解決策は、インスタンスのインポートを制御する Haskell のインポートメカニズムに拡張機能を追加することだと思います。それは問題を完全に解決するものではありませんが、すでに世の中に存在する孤児インスタンスによる被害から私たちのプログラムを保護するためのいくらかの助けになることでしょう。そして、時間が経てば、ある限られたケースでは、おそらくオーファン インスタンスはそれほど悪くないかもしれないと確信するかもしれません (そして、まさにその誘惑が、反オーファン インスタンス派の一部が私の提案に反対する理由なのです)。

これらすべてからの私の結論は、少なくとも当分の間、他の理由がないとしても他の人に配慮して、オーファン インスタンスを宣言しないことを強く勧めるということです。そのためには newtype .