いくつかの方法があります。どちらが最も適切かを判断するのに十分なコンテキストを提供していないと思います. GHC-7.4 を使用している場合は、このDefaultSignatures
拡張機能を試してみてください。
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DefaultSignatures #-}
-- A generic class with a generic function.
class Foo a where
foo :: a -> a
default foo :: Bar a => a -> a
foo = bar . baz
-- A specific class with specific functions.
class Bar a where
bar :: a -> a
baz :: a -> a
instance Bar String where
bar = id
baz = id
instance Foo String
main :: IO ()
main =
putStrLn (foo "bar")
型が のインスタンスであることを宣言する必要がありますがFoo
、既定の実装が使用されるため、メソッド宣言を繰り返す必要はありません。
別のかなり軽量なアプローチは、newtype を使用することです。インスタンスを必要とする関数がある場合は、インスタンスを newtype でFoo
ラップできます。Bar
newtype FooBar a = FooBar { unFooBar :: a }
instance Bar a => Foo (FooBar a) where
foo = FooBar . bar . baz . unFooBar
-- imported from a library or something...
needsFoo :: Foo a => a -> b
myFunc = needsFoo (FooBar someBar)
foo
または、通常の関数に置き換えるか、Bar
インスタンス用に特化したバージョンを作成することでうまくいく場合があります。
-- if every `Foo` is also a `Bar`, you can just do this. No need for `Foo` at all!
foo :: Bar a => a -> a
foo = bar . baz
-- if most `Foo`s aren't `Bar`s, you may be able to use this function when you have a `Bar`
fooBar :: Bar a => a -> a
foo = bar . baz
これらは、状況に適している場合、おそらく最良の解決策です。
もう 1 つのオプションは、すべてのFoo
インスタンスを手動で宣言することです。考えられるさまざまなインスタンスが多数存在する可能性がありますが、実際に使用されるインスタンスがほんの一握りしかないコードベースはかなり一般的です。ここでそれが本当なら、より一般的な解決策を実装しようとするよりも、必要な 3 つまたは 4 つのインスタンスを書き出す方がおそらく簡単です。
最後の手段として、元のコードのようなものを使用できますが、OverlappingInstances
それを機能させる必要もあります ( が必要OverlappingInstances
ない場合は、Foo
クラスは必要ありません)。これは、使用可能な一致が複数ある場合に、GHC が「最も具体的なインスタンス」を選択できるようにする拡張機能です。これは多かれ少なかれ機能しますが、期待どおりにならない場合があります。
class Foo a where
foo :: a -> a
class Bar a where
bar :: a -> a
baz :: a -> a
instance Bar String where
bar = id
baz = id
instance Bar a => Foo a where
foo = bar . baz
instance Foo [a] where
foo _ = []
main :: IO ()
main =
print (foo "foo")
main
空の文字列を出力するようになりました。と の 2 つのFoo
インスタンスがa
あります[a]
。後者はより具体的であるため、おそらく前者が必要だったとしてもfoo "foo"
、文字列には type があるため、後者が選択されます。[Char]
だから今、あなたも書く必要があります
instance Foo String where
foo = bar . baz
その時点で、Bar a => Foo a
インスタンスを完全に除外することもできます。