型クラス がありMyClass
、その中に を生成する関数がありますString
。これを使用して のインスタンスを暗示し、 を実装する型をShow
渡せるようにしたいと思います。これまでのところ、MyClass
show
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
これにより、エラー「Class
MyClass' が型として使用されています」が発生します。
この関係を 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' はどの型にも一致するからです。多くの点で、'インスタンス' と 'クラス' の定義の構文は逆になっています。