Iba a editar mi otra publicación, pero esto es lo suficientemente grande como para que funcione.
He aquí una forma de hacerlo con "tipo mágico", pero me parece que es algo subóptimo, ya que requiere una función de elevación que es específica para funciones de un número particular de argumentos (más abajo).
Vamos a empezar por definir un tipo de datos recursiva
data RecT a = RecR a
| RecC (a -> RecT a)
Así variables de tipo RecT puede ser o bien sólo el resultado envuelta (RecR) o pueden ser una recursividad continua (RecC).
Ahora, ¿cómo tomamos algo y lo convertimos en un tipo RecT a?
Los valores son fáciles:
valRecT x = RecR x
RecR x es obviamente de tipo RecT a.
¿Qué pasa con una función que toma un argumento, como id?
idRecT x = RecC $ \x -> RecR x
RecC envuelve una función que toma una variable y vuelve escriba un RecT. La expresión
\x -> RecR x
es solo una de esas funciones, ya que como hemos observado anteriormente, RecR x es del tipo RecT a.
De manera más general, cualquier función de un argumento se puede levantar:
lift1RecT :: (a -> a) -> RecT a
lift1RecT fn = RecC $ \a -> RecR $ fn a
podemos generalizar esto, envolviendo varias veces la función más profundamente anidados llamadas dentro RecC:
lift2RecT :: (a -> a -> a) -> RecT a
lift2RecT fn = RecC $ \b -> RecC $ \a -> RecR $ fn b a
lift3RecT :: (a -> a -> a -> a) -> RecT a
lift3RecT fn = RecC $ \c -> RecC $ \b -> RecC $ \a -> RecR $ fn c b a
OK, por lo que He hecho todo este trabajo para convertir una función de un número arbitrario de argumentos en un solo tipo, RecT a. ¿Cómo usamos esto?
Podemos escribir fácilmente abajo un nivel de aplicación de función:
reduceRecT :: RecT a -> a -> RecT a
reduceRecT (RecC fn) = fn
reduceRecT _ = undefined
En otras palabras, reduceRecT toma un argumento de tipo RecT una y otra de tipo A y devuelve una nueva RecT una que ha sido un nivel reducido .
También podemos desenrollar un cálculo terminada dentro de un RecT en el resultado:
unrollRecT :: RecT a -> a
unrollRecT (RecR fn) = fn
unrollRecT _ = undefined
Ahora estamos listos para aplicar una lista de argumentos a una función!
lApply :: [a] -> RecT a -> a
lApply [] fn = unrollRecT fn
lApply (l:ls) fn = lApply ls $ (reduceRecT fn) l
Consideremos el caso base primera: si hemos terminado con el cálculo, acabamos de desenvolver el resultado y lo devuelve.En el caso recursivo, reducimos la lista de argumentos en uno, luego transformamos el fn aplicando el encabezado de la lista al fn reducido, lo que da como resultado un nuevo RecT a.
Vamos a darle a esto un intento:
lApply [2,5] $ lift2RecT (**)
> 32.0
Así, ventajas y desventajas de este enfoque? Bueno, la versión de Template Haskell puede hacer una aplicación de lista parcial; esto no es cierto para la solución de tipo isorecursivo como se presenta aquí (aunque en principio podríamos arreglar esto con algo de fealdad). La solución tipo también tiene la desventaja de tener un código mucho más repetitivo asociado: necesitamos una listaNRecT para todas las N que queremos usar. Finalmente, es mucho menos fácil generalizar esto a la solución de tupla análoga si queremos aplicar funciones de tipos de variables mixtas.
Por supuesto, otra posibilidad interesante es mejorar la brevedad mediante el uso de Template Haskell para generar las funciones listRecT; esto elimina algunos repetitivos, pero en cierto sentido adquiere las desventajas de ambas implementaciones.
Eche un vistazo rápido a las funciones polyvariadic, pero puede hacer que su mente: [http://stackoverflow.com/questions/3467279/how-to-create-a-polyvariadic-haskell-function] – fuz