2008-12-18 62 views
8

Viniendo de C++, encuentro que la programación genérica es indispensable. Me pregunto cómo las personas se acercan a eso en Haskell.¿Cómo se hace programación genérica en Haskell?

Digamos cómo escribir la función de intercambio genérico en Haskell?

¿Existe un concepto equivalente de especialización parcial en Haskell?

En C++, puedo parcialmente especializar la función de intercambio genérico con una especial para un contenedor genérico de mapa/hash_map que tiene un método de intercambio especial para el intercambio de contenedor O (1). ¿Cómo se hace eso en Haskell o cuál es el ejemplo canónico de programación genérica en Haskell?

+31

Esto es una broma, ¿verdad? Esto es como preguntar cómo puede tener un control preciso sobre todo en el ensamblaje: P – ShreevatsaR

+0

Nada que ver con el ensamblaje, realmente. Simplemente mantenga la misma interfaz con una familia de algoritmos. – obecalp

+3

Además de la extraña pregunta sobre la programación genérica y la especialización parcial (buscar currying), la pregunta sobre "intercambiar variables" también es extraña: no hay nada como "intercambiar el contenido de dos cuadros" en Haskell, porque las variables en Haskell son * no * cuadros con datos en ellos. – ShreevatsaR

Respuesta

27

Esto está estrechamente relacionado con su otra pregunta sobre Haskell y quicksort. Creo que probablemente necesites leer al menos la introducción de un libro sobre Haskell. Parece como si aún no hubiera captado el punto clave al respecto, que es que le impide modificar los valores de las variables existentes.

Swap (como se entiende y se usa en C++) es, por su propia naturaleza, todo sobre la modificación de los valores existentes. Es así que podemos usar un nombre para referirnos a un contenedor, y reemplazar ese contenedor con contenidos completamente diferentes, y especializar esa operación para que sea rápida (y sin excepciones) para contenedores específicos, lo que nos permite implementar un enfoque de modificar y publicar (crucial para escribir código de excepción o intentar escribir código sin bloqueo).

Puede escribir un intercambio genérico en Haskell, pero probablemente tomaría un par de valores y devolvería un nuevo par que contenga los mismos valores con sus posiciones invertidas, o algo así. No es realmente lo mismo, y no tiene los mismos usos. No tendría ningún sentido tratar de especializarlo para un mapa cavando dentro de ese mapa e intercambiando sus variables de miembros individuales, porque simplemente no se te permite hacer cosas como esas en Haskell (puedes hacer la especialización, pero no la modificación de variables).

Supongamos que queremos "medida", una lista en Haskell:

measure :: [a] -> Integer 

Esa es una declaración de tipo. Significa que la función measure toma una lista de cualquier cosa (a es un parámetro de tipo genérico porque comienza con una letra minúscula) y devuelve un Entero. Así que esto funciona para una lista de cualquier tipo de elemento: es lo que se llamaría una plantilla de función en C++, o una función polimórfica en Haskell (no es lo mismo que una clase polimórfica en C++).

Ahora podemos definir que al proporcionar especializaciones para cada caso interesante:

measure [] = 0 

es decir, medir la lista vacía y se obtiene cero.

Aquí es una definición muy general que abarca todos los demás casos:

measure (h:r) = 1 + measure r 

El bit entre paréntesis en la LHS es un patrón. Significa: tomar una lista, romper la cabeza y llamarla h, llamar a la parte restante r. Esos nombres son entonces parámetros que podemos usar. Esto coincidirá con cualquier lista con al menos un elemento en ella.

Si ha intentado la metaprogramación de plantillas en C++, todo esto será obsoleto, ya que involucra exactamente el mismo estilo: recurrencia para hacer bucles, especialización para terminar la recursión. Excepto que en Haskell funciona en tiempo de ejecución (especialización de la función para valores particulares o patrones de valores).

+0

swap es el ejemplo de canonical C++ gp. Puede intentar ilustrar el mismo concepto con un ejemplo canónico de Haskell también. He revisado la pregunta para reflejar eso. – obecalp

9

Como Earwicker sais, el ejemplo no es tan significativo en Haskell. Si a pesar de todo quiere tener de todos modos, aquí es algo similar (el canje de las dos partes de una pareja), c & p de una sesión interactiva:

GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> let swap (a,b) = (b,a) 
Prelude> swap("hello", "world") 
("world","hello") 
Prelude> swap(1,2) 
(2,1) 
Prelude> swap("hello",2) 
(2,"hello") 
+4

Las razones tienen en cuenta que esto no cambia los valores, sino que devuelve un nuevo par (dado que el par original es inmutable). – TheMarko

+0

Es muy similar a lo que ocaml hace. ¿Qué ocurre si quiero cambiar dos tablas hash o un mapa, cómo se asegura de que los valores no se copien? – obecalp

+0

Si desea operar en grandes tablas de estado, probablemente no desee utilizar programación funcional en primer lugar. Si es absolutamente necesario, existe IORef y la mónada de estado para realizar cálculos que mantienen un estado. – TheMarko

2

Después de leer lo suficiente en un libro de Haskell para comprender realmente la respuesta de Earwicker Sugiero que también leas sobre clases de tipos. No estoy seguro de qué significa "especialización parcial", pero parece que podrían acercarse.

6

En Haskell, las funciones son tan genéricas (polimórficas) como sea posible; el compilador deducirá el "tipo más general". Por ejemplo, el ejemplo de intercambio de TheMarko es polimórfico de forma predeterminada en la ausencia de una declaración de tipo:

* Inicio> deje de intercambio (a, b) = (b, a)
* Inicio>: t de intercambio
de intercambio: : (t, t1) -> (t1, t)

en cuanto a la especialización parcial, GHC tiene un no-98 extensión:
file:///C:/ghc/ghc-6.10.1/doc/users_guide/pragmas.html#specialize-pragma

también, nota que hay una falta de coincidencia en la terminología. Lo que se llama genérico en C++, Java y C# se llama polimórfico en Haskell. "Genérico" en Haskell generalmente significa politípico: http://haskell.readscheme.org/generic.html
Pero, antes uso el significado C++ de genérico.

+1

Tu enlace está muerto – Kos

+0

Gracias, ambos están muertos, ese primero ni siquiera es un enlace. Ese primero debería ser: [link] http://lambda.haskell.org/platform/doc/current/ghc-doc/users_guide/pragmas.html#specialize-pragma y el segundo se puede encontrar en archive.org: [enlace] http://web.archive.org/web/20080511185727/http://haskell.readscheme.org/generic.html –

5

En Haskell crearía clases de tipos. Las clases de tipo no son como las clases en los lenguajes OO. Tome la clase de tipo numérico. Dice que cualquier cosa que sea una instancia de la clase puede realizar ciertas operaciones (+ - * /), por lo que Integer es miembro de Numeric y proporciona implementaciones de las funciones necesarias para ser consideradas numéricas y se pueden usar en cualquier lugar. Se espera numérico.

Supongamos que quiere poder engañar Ints and Strings. Luego declararías Int y String como instancias de la clase de tipo Foo. Ahora, en cualquier lugar donde vea el tipo (Foo a), ahora puede usar Int o String.

La razón por la que no puede agregar entradas y flotantes directamente es porque add tiene el tipo (Numérico a) a -> a -> aa es una variable de tipo y al igual que las variables regulares solo puede vincularse una vez para tan pronto como lo vincule a Int, cada a en la lista debe ser Int.

Cuestiones relacionadas