Hay algunos enfoques que puede tomar; No creo que haya proporcionado suficiente contexto para determinar cuál sería el más apropiado. Si está utilizando GHC-7.4, es posible que desee probar la extensión 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")
usted todavía tiene que declarar que un tipo es una instancia de Foo
, pero no es necesario repetir la declaración de método porque se utiliza la implementación predeterminada.
Otro enfoque bastante ligero es utilizar un nuevo tipo. Si tiene funciones que necesitan una instancia Foo
, puede ajustar una instancia Bar
en el nuevo tipo.
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)
Alternativamente, es posible que pueda pasar con la sustitución de foo
con una función normal, o hacer una versión especializada para Bar
casos:
-- 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
Estas son probablemente las mejores soluciones si trabajan para tu situación.
Otra opción es declarar cada instancia de Foo
manualmente. Aunque puede haber muchas instancias diferentes concebibles, es bastante común que las bases de código solo tengan un puñado de instancias que realmente se usan. Si eso es cierto aquí, probablemente sea menos trabajo simplemente escribir las 3 o 4 instancias que necesita en lugar de intentar implementar una solución más general.
Como último recurso, puede utilizar algo así como su código original, pero también tendrá que OverlappingInstances
para que funcione (si es que no es necesario OverlappingInstances
, entonces usted no necesita una clase Foo
). Esta es la extensión que permite a GHC elegir la "instancia más específica" cuando hay múltiples coincidencias disponibles. Esto funcionará más o menos, aunque es posible que no obtenga lo que espera.
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")
Ahora main
imprime una secuencia vacía. Hay dos instancias Foo
, para a
y [a]
. Este último es más específico, por lo que se elige para foo "foo"
ya que una cadena tiene el tipo [Char]
, aunque es probable que desee la primera. Por lo que ahora también había necesidad de escribir
instance Foo String where
foo = bar . baz
momento en el que usted puede también dejar de lado la instancia Bar a => Foo a
por completo.
Al declarar la 'instancia Bar a => Foo a' ha introducido una instancia para cada 'a' - el contexto' Bar a' no se usa en la selección de instancias - aunque si tiene instancias superpuestas GHC encontrará la más específico uno por módulo. –
Eso es ... contador intuitivo :-) ¿Hay alguna forma de evitarlo? Supongo que "encontrar la instancia más específica" es lo que 'IndecisoInstancias' intentará hacer, pero en mi código falla miserablemente. –
Relacionado: http://stackoverflow.com/questions/3213490 – sdcvvc