Бананы на розовых кустах
Feb. 2nd, 2009 07:59 pmКогда я только разбирался с бананами, линзами и т.д. задача описания катаморфизма для розы:
вызывала у меня большое затруднение.
Мы с
mibori очень пытались понять, как это сделать правильно. Я там напутал со страшной силой. Вчера я разбирался с разными бла-морфизмами и то, как их получить, основываясь на их ТК определениях (initial F-algebra в нашем случае) - и я вдруг прозрел.
Всё оказывается очень просто. Пусть роза - это initial RA-algebra, которую мы назовём
Во-первых, поскольку мы работаем с initial RA-algebra, наш тип должен быть этим самым функтором RA. С Rose так и есть:
Во-вторых, конструктор у розы всего один:
поэтому нам нужна всего одна функция. Поскольку функтор RA определяется так:
где LA - это функтор, относящийся к списку:
то получаем, что нам нужна функция
μLC - это carrier LC-алгебры для списков.
Дальше. Катаморфизм есть уникальное решение (как следует из его universal property) следующего равенста:
Самое сложное здесь - понять почему мы используем L(|φ|). Я пока понимаю это интуитивно, подстановкой в типе функции
Ну, и всё! :-)
Теперь я чувствую, что работа с ТК приносит плоды. Могу, например, параморфизм расписать для розы, хе-хе. А ведь я ещё в самом начале ;-)
data Rose a = Rose a [Rose a]
вызывала у меня большое затруднение.
Мы с
Всё оказывается очень просто. Пусть роза - это initial RA-algebra, которую мы назовём
(μRA, in).Во-первых, поскольку мы работаем с initial RA-algebra, наш тип должен быть этим самым функтором RA. С Rose так и есть:
instance Functor Rose where
fmap f (Rose x xs) = Rose (f x) $ map f xs
Во-вторых, конструктор у розы всего один:
rose = in
поэтому нам нужна всего одна функция. Поскольку функтор RA определяется так:
RA(X) = A × LX(Y)
где LA - это функтор, относящийся к списку:
LA(X) = 1 + A × X
то получаем, что нам нужна функция
r : A × μLC -> C
μLC - это carrier LC-алгебры для списков.
Дальше. Катаморфизм есть уникальное решение (как следует из его universal property) следующего равенста:
(|φ|) . rose = r . (id × L(|φ|))
Самое сложное здесь - понять почему мы используем L(|φ|). Я пока понимаю это интуитивно, подстановкой в типе функции
r.Ну, и всё! :-)
foldRose r (Rose x xs) = r x (map (foldRose r) xs)
Теперь я чувствую, что работа с ТК приносит плоды. Могу, например, параморфизм расписать для розы, хе-хе. А ведь я ещё в самом начале ;-)
no subject
Date: 2009-02-03 10:20 am (UTC)А что делать? Если не компилится в голове, надо проверить на железяке.
Думаю, нерекурсивный функтор получается примерно так:
data FooF t a r = FooF (t r) -- заменили все вхождения Foo t a на r instance (Functor t) => Functor (FooF t a) where fmap f (FooF tr) = FooF (fmap f tr)no subject
Date: 2009-02-03 10:30 am (UTC)Надо
ну и тогда дальше просто
Ну и из ТК получается тоже точно так же. Спасибо, что направил на путь истиный :-)
no subject
Date: 2009-02-03 10:31 am (UTC)А то компилироваться не будет ;-) Надо бы проверять перед постом...
no subject
Date: 2009-02-03 10:53 am (UTC)ЭЭ. Не за что, я сам не понял, куда я тебя направил :).
>но у тебя функтор по r получился, а нам a нужно
ничего не понимаю. зачем нам a нужно?
data types a-la-carte как раз получаются из функторов по r.
На примере списков, как у Марка Джонса:
data ListF a r = Nil | Cons a r instance Functor (ListF a) where fmap f Nil = Nil fmap f (Cons a r) = Cons a (f r) data Fix f = In { out :: f (Fix f) } type List a = Fix (ListF a) fold f = f . fmap (fold f) . out sumList = fold f where f Nil = 0 f (Cons x xs) = x + xs test = sumList $ In $ Cons 3 $ In $ Cons 2 $ In $ Cons 1 $ In Nil -- sum [3,2,1] = 6no subject
Date: 2009-02-03 07:31 pm (UTC)получаем те же яйца, что и в рекурсивном типе.