function - tipos - ¿Haskell tiene varias funciones/tuplas?
string en haskell (3)
Encontrar maneras de falsificar este tipo de cosas utilizando trucos de sistema de tipo sobrecargado es uno de mis pasatiempos, así que confía en mí cuando digo que el resultado es bastante feo. En particular, tenga en cuenta que las tuplas no se definen recursivamente, por lo que no hay una forma real de abstraerse directamente sobre ellas; En lo que respecta al sistema de tipos de Haskell, cada tamaño de tupla es completamente distinto.
Por lo tanto, cualquier enfoque viable para trabajar con tuplas directamente requerirá la generación de código, ya sea utilizando TH o una herramienta externa como con el paquete de tuple
.
Para falsificarlo sin usar el código generado, primero debe recurrir al uso de definiciones recursivas, normalmente pares anidados a la derecha con un valor "nulo" para marcar el final, ya sea (,)
y ()
o algo equivalente a ellos. Puede notar que esto es similar a la definición de listas en términos de (:)
y []
- y, de hecho, las falsas tuplas definidas recursivamente de este tipo pueden verse como estructuras de datos de nivel de tipo (una lista de tipos ) o como listas heterogéneas (por ejemplo, HList funciona de esta manera).
Las desventajas incluyen, pero no se limitan a, el hecho de que usar las cosas construidas de esta manera puede ser más incómodo de lo que vale, el código para implementar los trucos del sistema de tipos suele ser desconcertante y completamente no portátil, y el resultado final no es necesariamente equivalente de todos modos - hay múltiples diferencias no triviales entre (a, (b, (c, ())))
y (a, b, c)
, por ejemplo.
Si quieres ver lo horrible que es, puedes mirar las cosas que tengo en GitHub , particularmente las partes here .
La función uncurry
solo funciona para funciones que toman dos argumentos:
uncurry :: (a -> b -> c) -> (a, b) -> c
Si quiero liberar funciones con un número arbitrario de argumentos, podría escribir funciones separadas:
uncurry2 f (a, b) = f a b
uncurry3 f (a, b, c) = f a b c
uncurry4 f (a, b, c, d) = f a b c d
uncurry5 f (a, b, c, d, e) = f a b c d e
Pero esto se vuelve tedioso rápidamente. ¿Hay alguna forma de generalizar esto, así que solo tengo que escribir una función?
Intente uncurryN
del paquete de la tuple . Como todas las formas de sobrecarga, se implementa utilizando clases de tipo. En este caso, al detallar manualmente las instancias de hasta 15 tuplas, esto debería ser más que suficiente.
Las funciones variables también son posibles usando clases de tipo. Un ejemplo de esto es Text.Printf . En este caso, se hace por inducción estructural en el tipo de función. Simplificado, funciona así:
class Foo t
instance Foo (IO a)
instance Foo b => Foo (a -> b)
foo :: Foo
No debería ser difícil ver que foo
puede ser instanciado para los tipos IO a
, a -> IO b
, a -> b -> IO c
y así sucesivamente. QuickCheck también utiliza esta técnica.
Sin embargo, la inducción estructural no funcionará en las tuplas, ya que un n- par no tiene ninguna relación con un n + 1 , por lo que las instancias deben explicarse de forma manual.
No hay una forma sencilla de escribir una definición única de " uncurry
que funcione para diferentes números de argumentos.
Sin embargo, es posible usar la Plantilla Haskell para generar las muchas variantes diferentes que de otra manera tendría que escribir a mano.