Катаморфизмы для церковных списков
Я хочу быть в состоянии использовать cata
от recursion-schemes
пакет для списков в церковной кодировке.
type ListC a = forall b. (a -> b -> b) -> b -> b
Я использовал второй тип ранга для удобства, но мне все равно. Не стесняйтесь добавлять newtype
используйте ГАДЦ и т. д., если считаете это необходимым.
Идея церковного кодирования широко известна и проста:
three :: a -> a -> a -> List1 a
three a b c = \cons nil -> cons a $ cons b $ cons c nil
В основном "абстрактный неопределенный" cons
а также nil
используются вместо "нормальных" конструкторов. Я считаю, что все может быть закодировано таким образом (Maybe
, деревья и т. д.).
Легко показать, что List1
действительно изоморфен нормальным спискам:
toList :: List1 a -> [a]
toList f = f (:) []
fromList :: [a] -> List1 a
fromList l = \cons nil -> foldr cons nil l
Таким образом, его базовый функтор такой же, как у списков, и его можно реализовать project
для этого и использовать технику из recursion-schemes
,
Но я не мог, поэтому мой вопрос "как мне это сделать?". Для обычных списков я могу просто сопоставить шаблон:
decons :: [a] -> ListF a [a]
decons [] = Nil
decons (x:xs) = Cons x xs
Так как я не могу сопоставить шаблон с функциями, я должен использовать сгиб, чтобы деконструировать список. Я мог бы написать на основе сгиба project
для обычных списков:
decons2 :: [a] -> ListF a [a]
decons2 = foldr f Nil
where f h Nil = Cons h []
f h (Cons hh t) = Cons h $ hh : t
Однако мне не удалось адаптировать его для закодированных церковью списков:
-- decons3 :: ListC a -> ListF a (ListC a)
decons3 ff = ff f Nil
where f h Nil = Cons h $ \cons nil -> nil
f h (Cons hh t) = Cons h $ \cons nil -> cons hh (t cons nil)
cata
имеет следующую подпись:
cata :: Recursive t => (Base t a -> a) -> t -> a
Чтобы использовать его с моими списками, мне нужно:
- Чтобы объявить базовый тип функтора для списка, используйте
type family instance Base (ListC a) = ListF a
- Реализовать
instance Recursive (List a) where project = ...
Я терплю неудачу на обоих этапах.
1 ответ
newtype
Обертка оказалась решающим шагом, который я пропустил. Вот код вместе с образцом катаморфизма от recursion-schemes
,
{-# LANGUAGE LambdaCase, Rank2Types, TypeFamilies #-}
import Data.Functor.Foldable
newtype ListC a = ListC { foldListC :: forall b. (a -> b -> b) -> b -> b }
type instance Base (ListC a) = ListF a
cons :: a -> ListC a -> ListC a
cons x (ListC xs) = ListC $ \cons' nil' -> x `cons'` xs cons' nil'
nil :: ListC a
nil = ListC $ \cons' nil' -> nil'
toList :: ListC a -> [a]
toList f = foldListC f (:) []
fromList :: [a] -> ListC a
fromList l = foldr cons nil l
instance Recursive (ListC a) where
project xs = foldListC xs f Nil
where f x Nil = Cons x nil
f x (Cons tx xs) = Cons x $ tx `cons` xs
len = cata $ \case Nil -> 0
Cons _ l -> l + 1