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
。我们真正做的是证明 ProfunctorMonoid
和 pre-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
显然,每个 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
。我们真正做的是证明 ProfunctorMonoid
和 pre-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