profunctors 和箭头之间有什么关系?

What's the relationship between profunctors and arrows?

显然,每个 Arrow 都是一个 Strong profunctor. Indeed ^>> and >>^ correspond to lmap and rmap. And first' and second' are just the same as first and second. Similarly every ArrowChoice is also Choice

与箭相比,发音器缺少的是组合它们的能力。如果我们添加合成,我们会得到一个箭头吗?换句话说,如果一个(强)profunctor 也是一个 category,它是否已经是一个箭头?如果没有,还缺少什么?

What profunctors lack compared to arrows is the ability to compose them. If we add composition, will we get an arrow?

类群

这正是“Notions of Computation as Monoids," which unpacks a result from the (rather dense) "Categorical semantics for arrows”第 6 节中解决的问题。 "Notions" 是一篇很棒的论文,因为虽然它深入探讨了范畴论,但它 (1) 并不假设 reader 对抽象代数的了解不只是粗略的知识,并且 (2) 说明了大部分偏头痛-用 Haskell 代码归纳数学。我们可以在这里简要总结论文的第 6 节:

假设我们有

class Profunctor p where
  dimap :: (contra' -> contra) -> (co -> co') -> p contra co -> p contra' co'

你在 Haskell 中对读音符的沼泽标准、正负二分法编码。现在这个数据类型,

data (⊗) f g contra co = forall x. (f contra x) ⊗ (g x co)

Data.Profunctor.Composition 中实现,就像 profunctor 的合成一样。例如,我们可以证明一个合法实例 Profunctor:

instance (Profunctor f, Profunctor g) => Profunctor (f ⊗ g) where
  dimap contra co (f ⊗ g) = (dimap contra id f) ⊗ (dimap id co g)

由于时间和 space.

的原因,我们将亲手证明它是合法的

好的。现在是有趣的部分。假设我们这个类型类:

class Profunctor p => ProfunctorMonoid p where
  e :: (a -> b) -> p a b
  m :: (p ⊗ p) a b -> p a b

这是一种在 Haskell 中对 profunctor monoids 概念进行编码的方式,需要更多的手动操作。具体来说,这是幺半群类别 Pro 中的一个幺半群,它是函子类别 [C^op x C, Set] 的一个幺半群结构,其中 为张量,Hom 为单位。所以这里有很多超具体的数学术语需要解开,但为此你应该阅读这篇论文。

然后我们看到 ProfunctorMonoid 同构于 Arrow ... 几乎。

instance ProfunctorMonoid p => Category p where
  id = dimap id id
  (.) pbc pab = m (pab ⊗ pbc)

instance ProfunctorMonoid p => Arrow p where
  arr = e
  first = undefined

instance Arrow p => Profunctor p where
  lmap = (^>>)
  rmap = (>>^)

instance Arrow p => ProfunctorMonoid p where
  e = arr
  m (pax ⊗ pxb) = pax >> pxb

当然,我们在这里忽略了类型类法则,但正如论文所示,它们确实非常有效。

现在我说几乎是因为关键是我们无法实施 first。我们真正做的是证明 ProfunctorMonoidpre-arrows 之间的同构。论文称 Arrow 没有 first a 预箭头。然后它继续表明

class Profunctor p => StrongProfunctor p where
  first :: p x y -> p (x, z) (y, z)

class StrongProfunctor p => StrongProfunctorMonoid p where
  e :: (a -> b) -> p a b
  m :: (p ⊗ p) a b -> p a b

对于 Arrow 所需的同构是充分必要的。 "strong" 这个词来自类别理论中的一个特定概念,这篇论文的描述比我所能收集到的更好的文字和更丰富的细节。

总结一下:

  • profunctors 类别中的幺半群是前箭头,反之亦然。 (该论文的先前版本使用术语 "weak arrows" 而不是预箭头,我想这也可以。)

  • 强profunctors类别中的幺半群是箭头,反之亦然。

  • 由于 monad 是内函子类别中的幺半群,我们可以想到 SAT 类比 Functor : Profunctor :: Monad : Arrow。这是 concepts-of-computation-as-monoids 论文的真正主旨。

  • 幺半群和幺半群是随处可见的温和海洋生物,很遗憾有些学生即使在计算机科学或软件工程教育中也没有学习幺半群。

  • 范畴论很有趣。

  • Haskell 很有趣。

@haoformayor 的回答(和参考论文)是对基础范畴论​​的深刻见解——幺半群的范畴相当漂亮! - 但我认为一些代码向您展示了如何将 Arrow 变成 Strong Category,反之亦然,因为它们出现在各自的库中可能会成为有用的附录。

import Control.Arrow
import Control.Category
import Data.Profunctor
import Data.Profunctor.Strong
import Prelude hiding (id, (.))

一种方式...

newtype WrapP p a b = WrapP { unwrapP :: p a b }

instance Category p => Category (WrapP p) where
    id = WrapP id
    WrapP p . WrapP q = WrapP (p . q)

instance (Category p, Strong p) => Arrow (WrapP p) where
    first = WrapP . first' . unwrapP
    second = WrapP . second' . unwrapP

    -- NB. the first usage of id comes from (->)'s Category instance (id :: a -> a)
    -- but the second uses p's instance (id :: p a a)
    arr f = WrapP $ dimap f id id

...还有其他...

newtype WrapA p a b = WrapA { unwrapA :: p a b }

instance Arrow p => Profunctor (WrapA p) where
    dimap f g p = WrapA $ arr f >>> unwrapA p >>> arr g

instance Arrow p => Strong (WrapA p) where
    first' = WrapA . first . unwrapA
    second' = WrapA . second . unwrapA