我希望能够使用cata
包中的recursion-schemes
来获取Church编码中的列表。
type ListC a = forall b. (a -> b -> b) -> b -> b
为方便起见,我使用了第二种类型,但我并不在意。如果您认为有必要,可以随意添加newtype
,使用GADT等。
教会编码的概念众所周知且简单:
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 = ...
我在两个步骤都失败了。
答案 0 :(得分:4)
newtype
封面是我错过的关键步骤。以下是代码以及来自recursion-schemes
的示例catamorphism。
{-# 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