Я определил F-алгебру, согласно статьям Бартоша Милевского (один, два):
(Это не значит, что мой код является точным воплощением идей Бартоша, это просто мое ограниченное понимание их, и любые ошибки являются моими.)
module Algebra where
data Expr a = Branch [a] | Leaf Int
instance Functor Expr where
fmap f (Branch xs) = Branch (fmap f xs)
fmap _ (Leaf i ) = Leaf i
newtype Fix a = Fix { unFix :: a (Fix a) }
branch = Fix . Branch
leaf = Fix . Leaf
-- | This is an example algebra.
evalSum (Branch xs) = sum xs
evalSum (Leaf i ) = i
cata f = f . fmap (cata f) . unFix
Теперь я могу сделать почти все, что я хочу, например, суммировать листья:
λ cata evalSum $ branch [branch [leaf 1, leaf 2], leaf 3]
6
Это надуманный пример, который я специально сформулировал для этого вопроса, но на самом деле я попробовал некоторые менее тривиальные вещи (такие как оценка и упрощение полиномов с любым числом переменных), и он работает как шарм. Можно действительно сбрасывать и заменять какие-либо части структуры, поскольку каждый из них управляет катаморфизмом с помощью подходящей выбранной алгебры. Итак, я уверен, что F-Алгебра включает Foldable, и даже кажется, что она также распространяется на Traversable.
Теперь, могу ли я определить Foldable/Traversable экземпляры в терминах F-Алгебры?
Мне кажется, что я не могу.
- Я могу запустить катаморфизм только в исходной алгебре, которая является конструктором с нулевым типом. И алгебра, которую я ему даю, имеет тип
a b -> b
, а неa -> b
, т.е. Существует функциональная зависимость между типами "in" и "out". - Я не вижу
Algebra a => Foldable a
в любом месте сигнатур типа. Если это не сделано, это должно быть невозможно.
Мне кажется, что я не могу определить Foldable
в терминах F-алгебры по той причине, что Expr
должен быть Functor
в двух переменных: один для несущей, другой для значений и то a Foldable
во втором. Таким образом, может быть, что бифунтер более подходит. И мы можем построить также F-алгебру с бифунтером:
module Algebra2 where
import Data.Bifunctor
data Expr a i = Branch [a] | Leaf i
instance Bifunctor Expr where
bimap f _ (Branch xs) = Branch (fmap f xs)
bimap _ g (Leaf i ) = Leaf (g i)
newtype Fix2 a i = Fix2 { unFix2 :: a (Fix2 a i) i }
branch = Fix2 . Branch
leaf = Fix2 . Leaf
evalSum (Branch xs) = sum xs
evalSum (Leaf i ) = i
cata2 f g = f . bimap (cata2 f g) g . unFix2
Он работает следующим образом:
λ cata2 evalSum (+1) $ branch [branch [leaf 1, leaf 2], leaf 3]
9
Но я все еще не могу определить Складную. Он имел бы такой тип:
instance Foldable \i -> Expr (Fix2 Expr i) i where ...
К сожалению, для типов не существует лямбда-абстракций, и нет возможности сразу ввести подразумеваемую переменную типа в два места.
Я не знаю, что делать.