Ответ 1
Свободный функтор слева сопряжен с забывчивым функтором. Для присоединения вам нужно иметь изоморфизм (естественный в x
и y
):
(Free y :~> x) <-> (y :~> Forget x)
В какой категории это должно быть? Забывающий функтор забывает экземпляр Arrow
, поэтому он переходит из категории экземпляров Arrow
в категорию всех бифунтеров. И свободный функтор переходит в другую сторону, превращает любой бифунтер в свободный экземпляр Arrow
.
Тип стрелок типа Хеккеля в категории бифунторов:
type x :~> y = forall a b. x a b -> y a b
То же самое для стрелок в категории экземпляров Arrow
, но с добавлением ограничений Arrow
. Поскольку забывающий функтор только забывает об ограничении, нам не нужно представлять его в Haskell. Это превращает указанный изоморфизм в две функции:
leftAdjunct :: (FreeA x :~> y) -> x :~> y
rightAdjunct :: Arrow y => (x :~> y) -> FreeA x :~> y
leftAdjunct
также должен иметь ограничение Arrow y
, но, оказывается, он никогда не нужен в реализации. На самом деле очень простая реализация с точки зрения более полезного unit
:
unit :: x :~> FreeA x
leftAdjunct f = f . unit
unit
- ваш effect
и rightAdjunct
- ваш evalA
. Таким образом, у вас есть именно те функции, которые необходимы для присоединения! Вам нужно будет показать, что leftAdjunct
и rightAdjunct
являются изоморфными. Самый простой способ сделать это - доказать, что rightAdjunct unit = id
в вашем случае evalA effect = id
, что просто.
Как насчет analyze
? Это evalA
, специализированное для постоянной стрелки, с результирующим ограничением Monoid
, специализированным для аппликативного моноида. То есть.
analyze visit = getApp . getConstArr . evalA (ConstArr . Ap . visit)
с
newtype ConstArr m a b = ConstArr { getConstArr :: m }
и Ap
из пакет редукторов.
Изменить: я почти забыл, FreeA должен быть функтором более высокого порядка! Edit2: Который, с другой стороны, также может быть реализован с помощью rightAdjunct
и unit
.
hfmap :: (x :~> y) -> FreeA x :~> FreeA y
hfmap f = evalA (effect . f)
Кстати, существует еще один способ определения свободных функторов, для которого я недавно разместил пакет в Hackage. Он не поддерживает добрый * -> * -> *
(Edit: теперь он!), Но код можно адаптировать к свободным стрелкам:
newtype FreeA eff a b = FreeA { runFreeA :: forall arr. Arrow arr => (eff :~> arr) -> arr a b }
evalA f a = runFreeA a f
effect a = FreeA $ \k -> k a
instance Category (FreeA f) where
id = FreeA $ const id
FreeA f . FreeA g = FreeA $ \k -> f k . g k
instance Arrow (FreeA f) where
arr f = FreeA $ const (arr f)
first (FreeA f) = FreeA $ \k -> first (f k)
second (FreeA f) = FreeA $ \k -> second (f k)
FreeA f *** FreeA g = FreeA $ \k -> f k *** g k
FreeA f &&& FreeA g = FreeA $ \k -> f k &&& g k
Если вам не нужна интроспекция ваших предложений FreeA
, это FreeA
, вероятно, быстрее.