Библиотечная реализация схемы рекурсии

Я «изобрел» рекурсивную схему, являющуюся обобщением катаморфизма. Когда вы сворачиваете структуру данных с катаморфизмом, у вас нет доступа к подтермам, только к подрезультатам сворачивания:

{-# LANGUAGE DeriveFunctor #-}
import qualified Data.Map as M

newtype Fix f = Fix { unFix :: f (Fix f) }

cata :: Functor f => (f b -> b) -> Fix f -> b
cata phi = self where
    self = phi . fmap (\x -> self x) . unFix

Функция свертки phi имеет доступ только к результату self x, но не к исходному x. Поэтому я добавил функцию присоединения:

cataWithSubterm :: Functor f => (Fix f -> c -> b) -> (f b -> c) -> Fix f -> c
cataWithSubterm join phi = self
    where self = phi . fmap (\x -> join x (self x)) . unFix

Теперь можно осмысленно комбинировать x и self x, например, используя (,):

data ExampleFunctor a = Var String | Application a a deriving Functor

type Subterm = Fix ExampleFunctor

type Result = M.Map String [Subterm]

varArgs :: ExampleFunctor (Subterm, Result) -> Result
varArgs a = case a of
    Var _ -> M.empty
    Application ((Fix (Var var)), _) (arg, m) -> M.insertWith (++) var [arg] m

processTerm :: (ExampleFunctor (Subterm, Result) -> Result) -> Subterm -> Result
processTerm phi term = cataWithSubterm (,) phi term     

processTerm varArgs возвращает для каждого идентификатора список фактических аргументов, которые он получает по разным путям управления. Например. для bar (foo 2) (foo 5) возвращает fromList [("foo", [2, 5])]

Обратите внимание, что в этом примере результаты объединяются единообразно с другими результатами, поэтому я ожидаю существования более простой реализации с использованием производного экземпляра Data.Foldable. Но в целом это не так, поскольку phi может применять свои знания о внутренней структуре ExampleFunctor для объединения «подтерминов» и «промежуточных результатов» способами, невозможными в Foldable.

Мой вопрос: могу ли я построить processTerm, используя стандартные функции из современной библиотеки схем рекурсии, такой как recursion-schemes/Data.Functor.Foldable?


person nponeccop    schedule 02.08.2013    source источник
comment
ср. stackoverflow.com/questions/13317242/what-are-paramorphisms.   -  person Will Ness    schedule 02.08.2013


Ответы (1)


Сворачивание таким образом, что оно «съедает аргумент и сохраняет его», называется параморфизмом. Действительно, вашу функцию можно легко выразить с помощью схемы рекурсии как

cataWithSubterm :: Functor f => (Fix f -> b -> a) -> (f a -> b) -> Fix f -> b
cataWithSubterm f g = para $ g . fmap (uncurry f)

Более того, если мы снабдим (,) cataWithSubterm, как вы сделали в processTerm, мы получим

cataWithSubterm (,)  :: Functor f => (f (Fix f, b) -> b) -> Fix f -> b

который именно para специализируется на Fix:

para                 :: Functor f => (f (Fix f, b) -> b) -> Fix f -> b
person Petr    schedule 02.08.2013