您可以采取几种方法;我认为您没有提供足够的上下文来确定哪个是最合适的。如果您使用 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
,但您不需要重复方法声明,因为将使用默认实现。
另一种相当轻量级的方法是使用新类型。如果您有需要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
如果它们适合您的情况,这些可能是最好的解决方案。
另一种选择是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
打印一个空字符串。有两个Foo
实例, fora
和[a]
。后者更具体,因此选择它是foo "foo"
因为字符串具有 type [Char]
,尽管您可能想要前者。所以现在你还需要写
instance Foo String where
foo = bar . baz
在这一点上,您不妨完全忽略该Bar a => Foo a
实例。