haskell – Elegir entre una clase y un registro

Pregunta básica: ¿qué principios de diseño se deben seguir al elegir entre usar una clase o usar un registro (con campos polimórficos)?

Primero, sabemos que las clases y los registros son esencialmente equivalentes (ya que en Core, las clases se descuelgan a los diccionarios, que son solo registros). Sin embargo, hay diferencias: las clases se pasan implícitamente, los registros deben ser explícitos.

Mirando un poco más profundo, las clases son realmente útiles cuando:

> tenemos muchas representaciones diferentes de ‘lo mismo’, y
> en el uso real, se puede inferir qué representación se utiliza.

Las clases son incómodas cuando tenemos (hasta el polimorfismo paramétrico) solo una representación de nuestros datos, pero tenemos varias instancias. Esto conduce al ruido sintáctico de tener que usar newtype para agregar etiquetas adicionales (que existen solo en nuestro código, ya que sabemos que dichas etiquetas se borran en tiempo de ejecución) si no queremos activar todo tipo de extensiones problemáticas (es decir, instancias superpuestas y / o indecidibles).

Por supuesto, las cosas se vuelven más confusas: ¿qué sucede si quiero tener restricciones en mis tipos? Vamos a elegir un ejemplo real:

class (Bounded i, Enum i) => Partition a i where
    index :: a -> i

Yo podría haberlo hecho tan fácilmente

data Partition a i = Partition { index :: a -> i}

Pero ahora he perdido mis restricciones y, en su lugar, tendré que agregarlas a funciones específicas.

¿Hay pautas de diseño que me ayuden?

Tiendo a ver ningún problema con solo requerir restricciones en las funciones. El problema es, supongo, que su estructura de datos ya no modela precisamente lo que pretende. Por otro lado, si piensas en ella como una estructura de datos, en primer lugar, entonces eso debería importar menos.

Siento que no necesariamente tengo un buen conocimiento de la pregunta, y esto es tan vago como puede ser, pero mi regla de oro tiende a ser que las clases de tipos son cosas que obedecen a leyes (o significados de modelos) y tipos de datos. Son cosas que codifican una cierta cantidad de información.

Cuando queremos estratificar el comportamiento en formas complejas, descubrí que las clases de tipos comienzan de forma atractiva, pero que pueden ser dolorosas rápidamente y el cambio a pasar de diccionario hace que las cosas sean más sencillas. Es decir, cuando queremos que las implementaciones sean interoperables, debemos recurrir a un tipo de diccionario uniforme.

Esto es tomar dos, expandir un poco en un ejemplo concreto, pero aún así es solo una especie de ideas giratorias …

Supongamos que queremos modelar distribuciones de probabilidad sobre los reales. Dos representaciones naturales vienen a la mente.

A) Clases de clase

class PDist a where
        sample :: a -> Gen -> Double

B) impulsado por diccionario

data PDist = PDist (Gen -> Double)

Lo primero nos deja hacer

data NormalDist = NormalDist Double Double -- mean, var
instance PDist NormalDist where...

data LognormalDist = LognormalDist Double Double
instance PDist LognormalDist where...

Este último nos deja hacer

mkNormalDist :: Double -> Double -> PDist...
mkLognormalDist :: Double -> Double -> PDist...

En el primero, podemos escribir.

data SumDist a b = SumDist a b
instance (PDist a, PDist b) => PDist (SumDist a b)...

en este último podemos simplemente escribir

sumDist :: PDist -> PDist -> PDist

Entonces, ¿qué son las compensaciones? Typeclass-driven nos permite especificar qué distribuciones nos dan. La desventaja es que tenemos que construir un álgebra de distribuciones explícitamente, incluyendo nuevos tipos para sus combinaciones. Los datos no nos permiten restringir las distribuciones que nos dan (o incluso si están bien formadas), pero a cambio podemos hacer lo que sea que queramos.

Además, podemos escribir un parseDist :: String – > PDista con relativa facilidad, pero tenemos que pasar por alguna angustia para hacer el equivalente para el enfoque de clase de tipos.

Así que esto es, en cierto sentido, la compensación estática / dinámica con tipo / sin tipo en otro nivel. Sin embargo, podemos darle un giro y argumentar que la clase de tipos, junto con las leyes algebraicas asociadas, especifica la semántica de una distribución de probabilidad. Y el tipo PDist puede convertirse en una instancia de la clase de tipos PDist. Mientras tanto, podemos resignarnos a usar el tipo PDist (en lugar de typeclass) en casi todas partes, mientras pensamos que es una torre de instancias y tipos de datos necesarios para usar la clase de tipos de manera más “rica”.

De hecho, incluso podemos definir la función PDist básica en términos de funciones de clase de tipos. es decir, mkNormalPDist m v = PDist (muestra $ NormalDist m v) Así que hay mucho espacio en el espacio de diseño para deslizarse entre las dos representaciones según sea necesario …

Dirección original:https://stackoverflow.com/questions/8106764/choosing-between-a-class-and-a-record

Por favor indique la dirección original:haskell – Elegir entre una clase y un registro