Sim, é isso para. Compare com catamorfismo ou 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
Algumas pessoas chamam os paramorfismos de "recursão primitiva" em contraste com os catamorfismos ( foldr) sendo "iteração".
Onde foldr's dois parâmetros recebem um valor calculado recursivamente para cada subobjeto recursivo dos dados de entrada (aqui, esse é o fim da lista), paraos parâmetros' s obtêm o subobjeto original e o valor calculado recursivamente a partir dele.
Um exemplo de função que é bem expresso paraé a coleção dos elementos adequados de uma lista.
suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []
de modo a
suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]
Possivelmente mais simples ainda é
safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing
em que o ramo "contras" ignora seu argumento calculado recursivamente e apenas retorna a cauda. Avaliado preguiçosamente, o cálculo recursivo nunca acontece e a cauda é extraída em tempo constante.
Você pode definir foldrusando com parabastante facilidade; é um pouco mais complicado para definir paraa partir foldr, mas é certamente possível, e todos devem saber como se faz!
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)
O truque para definir paracom foldré reconstruir uma cópia dos dados originais, de modo que tenhamos acesso a uma cópia da cauda em cada etapa, mesmo que não tenhamos acesso ao original. Ao final, snddescarta a cópia da entrada e dá apenas o valor de saída. Não é muito eficiente, mas se você está interessado em expressividade pura, não paraoferece mais do que foldr. Se você usar esta foldrversão codificada de para, então safeTaillevará um tempo linear, copiando a cauda elemento por elemento.
Então, é isso: paraé uma versão mais conveniente do foldrque lhe dá acesso imediato ao final da lista, bem como ao valor calculado a partir dela.
No caso geral, trabalhar com um tipo de dados gerado como o ponto de fixação recursivo de um functor
data Fix f = In (f (Fix f))
Você tem
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)
e, novamente, os dois são mutuamente definíveis, sendo paradefinidos catapelo mesmo truque de "fazer uma cópia"
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
Novamente, paranão é mais expressivo do que cata, mas mais conveniente se você precisar de acesso fácil às subestruturas da entrada.
Edit: Lembrei-me de outro bom exemplo.
Considere as árvores de pesquisa binárias fornecidas por Fix TreeFonde
data TreeF sub = Leaf | Node sub Integer sub
e tente definir a inserção para árvores de pesquisa binárias, primeiro como a cata, depois como a para. Você achará a paraversão muito mais fácil, já que em cada nó você precisará inserir em uma subárvore, mas preservar a outra como estava.
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs), penso eu.