「型クラス a の場合、この定義により a は b のインスタンスでもある」と書くにはどうすればよいですか。質問する

「型クラス a の場合、この定義により a は b のインスタンスでもある」と書くにはどうすればよいですか。質問する

型クラス がありMyClass、その中に を生成する関数がありますString。これを使用して のインスタンスを暗示し、 を実装する型をShow渡せるようにしたいと思います。これまでのところ、MyClassshow

class MyClass a where
    someFunc :: a -> a
    myShow :: a -> String 

instance MyClass a => Show a where
    show a = myShow a

Constraint is no smaller than the instance head.私も試してみましたが、エラーが発生します。

class MyClass a where
    someFunc :: a -> a
    myShow :: a -> String

instance Show (MyClass a) where
    show a = myShow a

これにより、エラー「ClassMyClass' が型として使用されています」が発生します。

この関係を Haskell で正しく表現するにはどうすればよいでしょうか? ありがとうございます。

これに続いて、型に基づいて特定の文字列を出力する の特定のインスタンスを追加したいと付け加えておきますMyClass。たとえば、

data Foo = Foo
data Bar = Bar

instance MyClass Foo where
    myShow a = "foo"

instance MyClass Bar where
    myShow a = "bar"

main = do
    print Foo
    print Bar

ベストアンサー1

私はこれまで提示された破綻した解決策に強く反対したいと思います。

instance MyClass a => Show a where
    show a = myShow a

インスタンス解決の仕組み上、このインスタンスを実行するのは非常に危険です。

=>インスタンスの解決は、 の左側に何があるのか​​にはまったく関係なく、各インスタンスの の右側でパターン マッチングを行うことで進行します=>

それらのインスタンスが重複しない場合は、これは素晴らしいことです。しかし、ここであなたが言っているのは、「これは、次のような場合に使用すべきルールです」ということです。インスタンスを表示します。任意の型のインスタンスを表示するように求められたら、MyClass のインスタンスが必要になります。そのため、それを取得します。これが実装です。」 -- コンパイラがインスタンスを使用する選択をコミットすると、(「a」がすべてのものと統合されるという事実のみに基づいて) フォールバックして他のインスタンスを使用する機会はありません。

などをオンにして{-# LANGUAGE OverlappingInstances, IncoherentInstances #-}コンパイルすると、この定義を提供するモジュールをインポートし、他の Show インスタンスを使用する必要があるモジュールを記述するときに、それほど微妙ではないエラーが発生します。最終的には、十分な拡張機能を使用してこのコードをコンパイルできるようになりますが、残念ながら、期待どおりには動作しません。

考えてみれば、

instance MyClass a => Show a where
    show = myShow

instance HisClass a => Show a where
    show = hisShow

コンパイラはどれを選択すべきでしょうか?

あなたのモジュールはこれらのうちの1つしか定義していないかもしれませんが、エンドユーザーのコードはあなたのモジュールだけでなく、多くのモジュールをインポートします。また、別のモジュールが定義している場合

instance Show HisDataTypeThatHasNeverHeardOfMyClass

コンパイラが自分のインスタンスを無視して、自分のインスタンスを使用しようとするのは当然の権利です。

残念ながら、正しい答えは 2 つのことを行うことです。

それぞれ個人MyClassのインスタンスは、非常に機械的な定義でShowの対応するインスタンスを定義することができます。

instance MyClass Foo where ...

instance Show Foo where
    show = myShow

これはかなり残念なことですが、検討中の MyClass のインスタンスが少数しかない場合にはうまく機能します。

インスタンスが多数ある場合、コードの重複を避ける方法 (クラスが show よりもかなり複雑な場合) は、define を使用することです。

newtype WrappedMyClass a = WrapMyClass { unwrapMyClass :: a }

instance MyClass a => Show (WrappedMyClass a) where
    show (WrapMyClass a) = myShow a

これにより、インスタンスディスパッチの手段としてニュータイプが提供されます。そして

instance Foo a => Show (WrappedFoo a) where ...
instance Bar a => Show (WrappedBar a) where ...

と の型「パターン」が互いに素であるため、これは明確WrappedFoo aですWrappedBar a

パッケージ内にはこの慣用句の例が数多くありますbase

Control.Applicative には、まさにこの理由からWrappedMonad、 との定義があります。WrappedArrow

理想的には次のように言えるでしょう:

instance Monad t => Applicative t where
    pure = return
    (<*>) = ap 

しかし、このインスタンスが実際に言っているのは、すべての Applicative は、まず Monad のインスタンスを見つけて、それにディスパッチすることによって導出される必要があるということです。したがって、すべての Monad は Applicative であると言っているつもりですが (暗示のようなものを=>読むと)、実際には、すべての Applicative は Monad であると言っています。なぜなら、インスタンス ヘッド 't' はどの型にも一致するからです。多くの点で、'インスタンス' と 'クラス' の定義の構文は逆になっています。

おすすめ記事