Подтвердить что ты не робот

Методы 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'

Если так, это допускает функциональные зависимости?

4b9b3361

Ответ 1

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; линейные карты являются важным примером.

Ответ 2

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 в качестве суперкласса).