Когда дело доходит до применения теории категорий для общего программирования, Haskell выполняет очень хорошую работу, например, с библиотеками типа recursion-schemes
. Однако одна вещь, о которой я не уверен, заключается в том, как создать общий экземпляр functor для полиморфных типов.
Если у вас есть полиморфный тип, например List или Tree, вы можете создать функтор из (Hask × Hask) в Hask, который их представляет. Например:
data ListF a b = NilF | ConsF a b -- L(A,B) = 1+A×B
data TreeF a b = EmptyF | NodeF a b b -- T(A,B) = 1+A×B×B
Эти типы являются полиморфными на A, но являются фиксированными точками относительно B, что-то вроде этого:
newtype Fix f = Fix { unFix :: f (Fix f) }
type List a = Fix (ListF a)
type Tree a = Fix (TreeF a)
Но, как известно, списки и деревья также являются функторами в обычном смысле, где они представляют собой "контейнер" из a
, который можно сопоставить функции f :: a -> b
, чтобы получить контейнер b
"s.
Я пытаюсь выяснить, есть ли способ сделать эти типы (неподвижные точки) экземпляром Functor
общим способом, но я не уверен, как это сделать. До сих пор я столкнулся с двумя следующими проблемами:
1) Во-первых, должен быть способ определить общий gmap
по любой полиморфной неподвижной точке. Зная, что такие типы, как ListF
и TreeF
, являются Bifunctors, до сих пор я получил это:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Bifunctor
newtype Fix f = Fix { unFix :: f (Fix f) }
cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = f . fmap (cata f) . unFix
-- To explicitly use inF as the initial algebra
inF :: f (Fix f) -> Fix f
inF = Fix
gmap :: forall a b f. Bifunctor f => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
where
alg :: f a (Fix (f b)) -> Fix (f b)
alg = inF . bimap f id
В Haskell это дает мне следующую ошибку: Could not deduce (Functor (f a)) arising from a use of cata from the context (Bifunctor f)
.
Я использую пакет bifunctors
, который имеет тип WrappedBifunctor
, который специально определяет следующий экземпляр, который мог бы решить указанную выше проблему: Bifunctor p => Functor (WrappedBifunctor p a)
. Однако я не уверен, как "поднять" этот тип внутри Fix
, чтобы иметь возможность использовать его
2) Даже если общее определение gmap
может быть определено, я не знаю, возможно ли создать общий экземпляр Functor
, который имеет fmap = gmap
, и может мгновенно работайте для типов List
и Tree
там (как и любой другой тип, определенный аналогичным образом). Возможно ли это?
Если да, возможно ли сделать это совместимым с recursion-schemes
тоже?