2011-08-26 13 views
7

Tengo problemas para usar elegantemente el sistema de tipos de Haskell. Estoy seguro de que mi problema es común, pero no sé cómo describirlo, excepto en términos específicos de mi programa.Lograr las abstracciones correctas con el sistema de tipos de Haskell

Los conceptos que estoy tratando de representar son:

  • puntos de datos, cada uno de los cuales lleva una de varias formas, por ejemplo, (id, número de casos, número de controles), (id, número de casos, población)

  • conjuntos de puntos de datos e información agregada: (conjunto de id., casos totales, controles totales), con funciones para agregar/eliminar puntos (por lo que para cada variedad de punto, hay una variedad correspondiente de conjunto)

que podría tener una clase de tipos de punto y definir cada variedad de punto como su propio tipo. Alternativamente, podría tener un tipo de punto y un constructor de datos diferente para cada variedad. Del mismo modo para los conjuntos de puntos.

que tienen al menos una preocupación cada enfoque:

  • Con clases de tipo: Evitar el nombre de la función de colisión será molesto. Por ejemplo, ambos tipos de puntos podrían usar una función para extraer "número de casos", pero la clase de tipo no puede requerir esta función porque es posible que otros tipos de puntos no tengan casos.

  • Sin clases de tipos: Prefiero no exportar los constructores de datos desde, digamos, el módulo de puntos (proporcionando otras funciones más seguras para crear un nuevo valor). Sin los constructores de datos, no podré determinar de qué variedad es un valor de Punto dado.

¿Qué diseño puede ayudar a minimizar estos (y otros) problemas?

+0

¿En qué circunstancias es lo que necesita para tratar los puntos de datos de manera similar? Usted dice que cada formulario necesitará su propia variedad de conjuntos, y que las operaciones como "número de casos" no son universales. –

Respuesta

4

Para ampliar un poco la respuesta de sclv, hay una familia extensa de conceptos estrechamente relacionados que equivalen a proporcionar algunos medios para deconstruir un valor: catamorfismos, que son pliegues generalizados; La codificación de la Iglesia, que representa datos por sus operaciones, y a menudo equivale a aplicar parcialmente un catamorfismo al valor que deconstruye; CPS se transforma, donde una codificación Church se asemeja a una coincidencia de patrón reificado que toma continuaciones separadas para cada caso; representando datos como una colección de operaciones que lo usan, generalmente conocido como programación orientada a objetos; y así.

En su caso, lo que parece que quieres es un resumen de un tipo, es decir, uno que no exportar su representación interna, pero no un sellado totalmente uno, es decir, que deja abierta la representación de funciones en el módulo eso lo define Este es el mismo patrón seguido de cosas como Data.Map.Map.Probablemente no desee ir a la ruta de clase de tipo, ya que parece que necesita trabajar con una variedad de puntos de datos, en lugar de una opción arbitraria de un único tipo de punto de datos.

Lo más probable es que una combinación de "constructores inteligentes" para crear valores y una variedad de funciones de deconstrucción (como se describió anteriormente) exportadas desde el módulo sea el mejor punto de partida. Yendo de allí, espero que la mayoría de los detalles restantes tengan un enfoque obvio a seguir.

3

Con esta última solución (no hay clases de tipo), puede exportar un catamorphism del tipo en lugar de los constructores ..

data MyData = PointData Double Double | ControlData Double Double Double | SomeOtherData String Double 

foldMyData pf cf sf d = case d of 
     (PointData x y) -> pf x y 
     (ControlData x y z) -> cf x y z 
     (SomeOtherData s x) -> sf s x 

esa manera usted tiene una manera de tirar de sus datos aparte en lo que quiera (incluyendo simplemente ignorar los valores y las funciones de aprobación que devuelven qué tipo de constructor usaste) sin proporcionar una forma general de construir tus datos.

2

Creo que el enfoque basado en las clases de tipos es mejor siempre que no mezcle diferentes puntos de datos en una sola estructura de datos.

El problema colisión de nombres que ha mencionado se puede resolver mediante la creación de una clase de tipo separado para cada campo distinto, como esto:

class WithCases p where 
    cases :: p -> NumberOfCases 
Cuestiones relacionadas