Data.Functor.Foldable
предоставляется пакетом рекурсивных схем . Тип apo
есть:
apo :: Corecursive t => (a -> Base t (Either t a)) -> a -> t
Здесь t
- это структура, генерируемая раскрытием, а Base t
- ее базовый функтор. Вообще говоря, базовый функтор представляет один уровень рекурсивной структуры, идея состоит в том, что, если мы неопределенно вложим его в себя, мы получим тип, эквивалентный типу всей структуры - фактически, это именно то, что Fix
из Data.Functor.Foldable
делает. (В мета-заметке, похоже, здесь нет вопросов и ответов конкретно о Base
в рекурсивных схемах ; может быть полезно иметь один.)
Base
для списков:
data ListF a b = Nil | Cons a b
Так apo
специализируется на:
apo @[_] :: (b -> ListF a (Either [a] b)) -> b -> [a]
Если мы хотим написать это без использования инфраструктуры рекурсивной схемы , мы можем использовать тот факт, что ListF a b
изоморфен Maybe (a, b)
:
Nil | Cons a b
Nothing | Just (a, b)
В терминах Maybe (a, b)
подпись будет:
apoList :: (b -> Maybe (a, Either [a] b)) -> b -> [a]
В коалгебре (то есть аргумент функции в apo
), Nothing
(или Nil
, в рекурсивных схемах версии) генерация списка должна быть остановлена закрыв его пустым хвостом. Вот почему вам все еще нужно Maybe
, даже если вы также используете Either
для короткого замыкания разворачивания другими способами.
Реализация этого apoList
очень похожа на ту, что в вашем вопросе, за исключением того, что эта подпись не ограничивает начальное число (типа b
) списком, а переворачивает роли Left
и Right
(так что Left
сигнализирует короткое замыкание):
apoList :: (b -> Maybe (a, Either [a] b)) -> b -> [a]
apoList f b = case f b of
Nothing -> []
Just (x, Left e) -> x : e
Just (x, Right c) -> x : apoList f c