Методы 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 как суперкласс).

Другие вопросы по тегам