Me encuentro corriendo un problema comúnmente, al escribir programas más grandes en Haskell. A menudo me encuentro con varios tipos distintos que comparten una representación interna y varias operaciones principales.¿Maneja múltiples tipos con la misma representación interna y un texto estándar mínimo?
Existen dos enfoques relativamente obvios para resolver este problema.
Uno está utilizando una clase de tipo y la extensión GeneralizedNewtypeDeriving
. Ponga suficiente lógica en una clase de tipo para admitir las operaciones compartidas que el caso de uso desee. Cree un tipo con la representación deseada y cree una instancia de la clase de tipo para ese tipo. Luego, para cada caso de uso, cree envoltorios para él con newtype y obtenga la clase común.
La otra es declarar el tipo con una variable de tipo fantasma, y luego usar EmptyDataDecls
para crear distintos tipos para cada caso de uso diferente.
Mi principal preocupación no es mezclar los valores que comparten la representación interna y las operaciones, pero tienen diferentes significados en mi código. Ambos enfoques resuelven ese problema, pero se sienten significativamente torpes. Mi segunda preocupación es reducir la cantidad de repetidores requeridos, y ambos enfoques funcionan bastante bien.
¿Cuáles son las ventajas y desventajas de cada enfoque? ¿Hay alguna técnica que se acerque a hacer lo que quiero, proporcionando seguridad de tipo sin un código repetitivo?
Si la memoria me sirve, 'datos Foo a = Foo a',' datos Foo ab = Foo a', y 'newtype Bar a = Bar (Foo a)' (con el primer 'Foo') deberían compilar todos al mismo representación en tiempo de ejecución, por lo que encontrar una diferencia no trivial en el rendimiento sería algo inesperado. –
@camccann ¡La belleza de ghc-core y Criterion es una prueba empírica para complementar la memoria! :) Creo que la pregunta sobre el rendimiento tiene más que ver con si las operaciones provenientes de una clase afectan o no su rendimiento en comparación con la representación en tiempo de ejecución del valor en sí. Las funciones polimórficas van desde '(General a, General b) => a -> b -> Int' a' General2 a -> General2 b -> Int'. – Anthony