Sí, eso es para. Compare con catamorfismo, o foldr:
para :: (a -> [a] -> b -> b) -> b -> [a] -> b
foldr :: (a -> b -> b) -> b -> [a] -> b
para c n (x : xs) = c x xs (para c n xs)
foldr c n (x : xs) = c x (foldr c n xs)
para c n [] = n
foldr c n [] = n
Algunas personas llaman a los paramorfismos "recursividad primitiva" en contraste con los catamorfismos ( foldr) que son "iteración".
Donde foldrlos dos parámetros reciben un valor calculado de forma recursiva para cada subobjeto recursivo de los datos de entrada (aquí, esa es la cola de la lista), paralos parámetros obtienen tanto el subobjeto original como el valor calculado de forma recursiva a partir de él.
Una función de ejemplo que se expresa muy bien con paraes la colección de los suficientes suficientes de una lista.
suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []
así que eso
suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]
Posiblemente más simple aún es
safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing
en el que la rama "contras" ignora su argumento calculado de forma recursiva y simplemente devuelve la cola. Evaluado con pereza, el cálculo recursivo nunca ocurre y la cola se extrae en tiempo constante.
Puede definir foldrusandopara bastante facilidad; que es un poco más difícil de definir paraa partir foldr, pero es ciertamente posible, y todo el mundo debe saber cómo se hace!
foldr c n = para (\ x xs t -> c x t) n
para c n = snd . foldr (\ x (xs, t) -> (x : xs, c x xs t)) ([], n)
El truco para definir paracon foldres reconstruir una copia de los datos originales, de modo que obtengamos acceso a una copia de la cola en cada paso, aunque no tuviéramos acceso al original. Al final, snddescarta la copia de la entrada y da solo el valor de salida. No es muy eficiente, pero si te interesa la expresividad pura, parano te da más quefoldr . Si usa esta foldrversión codificada de para, luego safeTailtomará un tiempo lineal después de todo, copiando la cola elemento por elemento.
Entonces, eso es todo: paraes una versión más conveniente de la foldrcual le brinda acceso inmediato al final de la lista, así como al valor calculado a partir de ella.
En el caso general, trabajar con un tipo de datos generado como el punto fijo recursivo de un funtor
data Fix f = In (f (Fix f))
tienes
cata :: Functor f => (f t -> t) -> Fix f -> t
para :: Functor f => (f (Fix f, t) -> t) -> Fix f -> t
cata phi (In ff) = phi (fmap (cata phi) ff)
para psi (In ff) = psi (fmap keepCopy ff) where
keepCopy x = (x, para psi x)
y de nuevo, los dos son mutuamente definibles, paradefinidos catapor el mismo truco "hacer una copia"
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
De nuevo, para no es más expresivo cata, pero es más conveniente si necesita un fácil acceso a las subestructuras de la entrada.
Editar: Recordé otro buen ejemplo.
Considere los árboles de búsqueda binarios dados por Fix TreeFdonde
data TreeF sub = Leaf | Node sub Integer sub
e intente definir la inserción para árboles de búsqueda binarios, primero como a cata, luego como a para. Encontrará la paraversión mucho más fácil, ya que en cada nodo deberá insertar un subárbol pero conservar el otro tal como estaba.
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), me parece.