5

Chcę móc używać cata z pakietu dla list w kodowaniu Kościoła.Katamorfizmy dla list zakodowanych w kościele

type ListC a = forall b. (a -> b -> b) -> b -> b 

Dla wygody stosowałem typ drugiego rzędu, ale mnie to nie obchodzi. Jeśli uważasz, że jest to konieczne, dodaj numer newtype, użyj GADT itp.

Pomysł kodowania Kościele jest powszechnie znany i prosty:

three :: a -> a -> a -> List1 a 
three a b c = \cons nil -> cons a $ cons b $ cons c nil 

zasadzie „streszczenie nieokreślone” cons i nil są używane zamiast „normalnych” konstruktorów. Wierzę, że wszystko może być zakodowane w ten sposób (Maybe, drzewa itp.).

Łatwo pokazać, że List1 rzeczywiście jest izomorficzna normalnych list:

toList :: List1 a -> [a] 
toList f = f (:) [] 

fromList :: [a] -> List1 a 
fromList l = \cons nil -> foldr cons nil l 

Więc jego podstawa funktor jest taka sama jak list, i powinno być możliwe do wdrożenia project dla niego i używać maszyn z recursion-schemes.

Ale nie mogłem, więc moje pytanie brzmi "jak to zrobić?". Dla normalnych list, mogę tylko wzór mecz:

decons :: [a] -> ListF a [a] 
decons [] = Nil 
decons (x:xs) = Cons x xs 

Ponieważ nie wzór meczu na funkcji może muszę użyć krotnie dekonstrukcji listę. Mógłbym napisać krotny oparte project dla normalnych list:

decons2 :: [a] -> ListF a [a] 
decons2 = foldr f Nil 
    where f h Nil = Cons h [] 
     f h (Cons hh t) = Cons h $ hh : t 

Jednak nie udało mi się dostosować go do listy kościelnych zakodowane:

-- 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 ma następujący podpis:

cata :: Recursive t => (Base t a -> a) -> t -> a 

Aby używać go z moimi listami, potrzebuję:

  1. Aby zadeklarować typ bazowy funktora na liście przy użyciu type family instance Base (ListC a) = ListF a
  2. celu realizacji instance Recursive (List a) where project = ...

zawiodę w obu etapach.

+0

Jaka jest Twoja wina? Próbowałem tego (dodając "newtype" dla wygody) i działa dobrze. – MigMit

+0

Zaktualizowałem moje pytanie: – nponeccop

+1

"Nowy typ" okazał się niezbędny, a nie tylko udogodnieniem. Kod działa teraz. – nponeccop

Odpowiedz

4

Opakowanie newtype okazało się kluczowym krokiem, który przeoczyłem. Oto kod wraz z przykładowym katamorfizmem z 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 
Powiązane problemy