Методы Bifunctor vs. Arrow
Существует небольшое совпадение между Bifunctor
а также Arrow
методы:
class Bifunctor p where
first :: (a -> a') -> p a b -> p a' b
second :: (b -> b') -> p a b -> p a b'
bimap :: (a -> a') -> (b -> b') -> p a b -> p a' b'
class Arrow (~~>) where
...
first :: (a ~~> a') -> (a, b) ~~> (a', b)
second :: (b ~~> b') -> (a, b) ~~> (a, b')
(***) :: (a ~~> a') -> (b ~~> b') -> (a, b) ~~> (a', b')
Bifunctor
класс приходит с законами, полностью аналогичными тем из Functor
,
Arrow
Класс приходит с рядом законов, различных законов и несколько загадочным предупреждением о (***)
: "Обратите внимание, что это вообще не функтор." Удивительно (для меня) есть только один закон о (***)
:
first f >>> arr (id *** g) = arr (id *** g) >>> first f
Arrow (->)
экземпляр и Bifunctor (,)
экземпляр точно совпадает, так что bimap @(,) = (***) @(->)
, Есть ли какое-то особое значение для этого? Есть ли значимые гипотетические
class Foo (~~>) p where
biFoo :: (a ~~> a') -> (b ~~> b') -> p a b ~~> p a' b'
Если так, это допускает функциональные зависимости?
2 ответа
Arrow
является (несколько ублюденным) предшественником класса декартовых замкнутых категорий или наименее декартовых моноидальных категорий. В частности, к моноидальным категориям, тензорное произведение которых (,)
и единичный элемент ()
,
Напомним, что моноидальная категория характеризуется тензорным произведением как бифунктором, поэтому существует связь между Arrow
а также Bifunctor
,
***
на самом деле имеет больше законов, чем вы перечислили, только библиотека решает сформулировать их с точки зрения first
вместо. Вот эквивалентное определение класса:
class (Category k, Category k') => EnhancedCategory k k' where
arr :: k a b -> k' a b
-- arr id ≡ id
-- arr (f . g) = arr f . arr g
class (EnhancedCategory (->) a) => Arrow a where
(***) :: a b c -> a b' c' -> a (b,b') (c,c')
-- (f***id) . (g***id) ≡ (f.g)***id
-- (id***f) . (id***g) ≡ id***(f.g)
-- arr fst . (f***id) ≡ f . arr fst
-- arr snd . (id***g) ≡ g . arr snd
-- ¿ arr swap . (f***g) ≡ (g***f) . arr swap ?
-- ((f***g)***h) . assoc ≡ assoc . (f***(g***h))
diag :: a b (b,b)
first :: Arrow a => a b c -> a (b,d) (c,d)
first f = f***id
second :: Arrow a => a b c -> a (d,b) (d,c)
second g = id***g
(&&&) :: Arrow a => a b c -> a b d -> a b (c,d)
f&&&g = (f***g) . diag
Кстати, также можно удалить arr
для подъема чистых функций, и вместо этого дать суперклассу только специальные методы fst
, snd
а также assoc
, Я называю этот классCartesian
, Это позволяет определять категории "стрелок", которые не содержат произвольных функций Haskell; линейные карты являются важным примером.
Arrow
эквивалентно Strong
+ Category
,
Вы можете выбрать другое понятие силы, чтобы получить другой вид Arrow
,
class Category a => ArrowChoice a where
arr :: (b -> c) -> a b c
(+++) :: a b c -> a b' c' -> a (Either b b') (Either c c')
Другими словами, тензорное произведение вашей декартовой замкнутой категории не обязательно должно быть (,)
именно так. Любой тензорный продукт, который вы можете придумать, имеет соответствующее понятие силы, каждое из которых даст вам соответствующее разнообразие Arrow
,
Примечательно, что многие профессора оба Strong
а также Choice
, Так что ваши Foo
(который в основном обобщает Strong
над тензорным произведением p
) не имеет функциональной зависимости.
Control.Arrow
модуль в base
к сожалению, немного сбивает с толку иерархию (например, их ArrowChoice
имеет Arrow
как суперкласс).