笛卡尔(Profunctor)的例子?
Examples of Cartesian (Profunctor)?
我正在浏览以下代码示例,发现很难弄清楚如何使用 (->) 和 (Star f) 一旦他们实现 'first' 并成为 Cartisian 的成员。
谁能提供一些易于理解的例子?谢谢
-- Intuitively a profunctor is cartesian if it can pass around additional
-- context in the form of a pair.
class Profunctor p => Cartesian p where
first :: p a b -> p (a, c) (b, c)
first = dimap swapP swapP . second
second :: p a b -> p (c, a) (c, b)
second = dimap swapP swapP . first
instance Cartesian (->) where
first :: (a -> b) -> (a, c) -> (b, c)
first f = f `cross` id
instance Functor f => Cartesian (Star f) where
first :: Star f a b -> Star f (a, c) (b, c)
first (Star f) = Star $ (\(fx, y) -> (, y) <$> fx) . (f `cross` id)
注意,意见提前!
Profunctors 有点过于抽象。在我看来,我们应该首先讨论 类别 ;实际上,大多数发音者都是类别,但反之则不然。 profunctor class 可能有有效的用途,但它实际上更加有限并且与 Hask 类别相关。我更喜欢通过讨论其箭头构造函数在最后一个参数中是 Hask-函子和在 pænultimate 参数中是逆变 Hask-函子的类别来明确这一点.是的,这有点啰嗦,但这就是重点:这实际上是一个非常具体的情况,而且通常情况下你真的只需要一个不太具体的类别。
具体来说,Cartesian
更自然地被视为 class 类别,而不是 profunctors:
class Category k => Cartesian k where
swap :: k (a,b) (b,a)
(***) :: k a b -> k a' b' -> k (a,a') (b,b')
允许
first :: Cartesian k => k a b -> k (a,c) (b,c)
first f = f *** id
second :: Cartesian k => k a b -> k (c,a) (c,b)
second f = id *** f
这就是 category-agnostic id
。 (您还可以根据 first
、second f=swap.first f.swap
和 f***g=first f.second g
来定义 ***
和 second
,但在我看来这是不雅的纠缠。)
为了了解为什么我更喜欢这种方式,而不是使用 profunctors,我想举一个简单的例子,不是 profunctor:线性映射。
newtype LinearMap v w = LinearMap {
runLinearMap :: v->w -- must be linear, i.e. if v and w are finite-dimensional
-- vector spaces, the function can be written as matrix application.
}
这 不是 profunctor:尽管您可以使用此特定实现编写 dimag f g (LinearMap a) = LinearMap $ dimap f g a
,但这不会保持线性。然而,它是一个笛卡尔范畴:
instance Category LinearMap where
id = LinearMap id
LinearMap f . LinearMap g = LinearMap $ f . g
instance Cartesian LinearMap where
swap = LinearMap swap
LinearMap f *** LinearMap g = LinearMap $ f *** g
好的,这看起来很微不足道。为什么这很有趣?好吧,线性映射可以有效地存储为矩阵,但从概念上讲,它们主要是 函数 。因此,像处理函数一样处理它们是有意义的;在这种情况下,.
有效地实现了矩阵乘法,***
以类型安全的方式将 block diagonal matrix 放在一起。
显然,您也可以使用不受限制的函数来完成所有这些操作,所以 instance Cartesian (->)
真的很简单。但是我给出了线性映射示例来激励 Cartesian
class 可以做一些没有必要的事情。
Star
才是真正有趣的地方。
newtype Star f d c = Star{runStar :: d->f c}
instance Monad f => Category (Star f) where
id = Star pure
Star f . Star g = Star $ \x -> f =<< g x
instance Monad f => Cartesian (Star f) where
swap = Star $ pure . swap
Star f *** Star g = Star $ \(a,b) -> liftA2 (,) (f a) (g b)
Star
是 kleisli category 的前身,您可能听说过它是使用单子计算链接的一种方法。那么让我们直接来看一个 IO
示例:
readFile' :: Star IO FilePath String
readFile' = Star readFile
writeFile' :: Star IO (FilePath,String) ()
writeFile' = Star $ uncurry writeFile
现在我可以做类似的事情了
copyTo :: Star IO (FilePath, FilePath) ()
copyTo = writeFile' . second readFile'
我为什么要这样做?关键是我已经将 IO 操作链接在一起,而没有使用可以通过任何方式 peek/modify 传递的 data 的接口。这对于安全应用程序可能很有趣。 (我刚刚编造了这个例子;我相信可以找到不那么做作的例子。)
无论如何,到目前为止我还没有真正回答这个问题,因为你不是在问笛卡尔范畴,而是在问 strong profunctors。不过,它们确实提供了几乎相同的界面:
class Profunctor p => Strong p where
first' :: p a b -> p (a, c) (b, c)
second' :: p a b -> p (c, a) (c, b)
因此我也可以做出微小的改变
copyTo :: Star IO (FilePath, FilePath) ()
copyTo = writeFile' . second' readFile'
保留基本相同的示例,但使用 Strong
而不是 Cartesian
。不过,我仍在使用 Category
组合。而且我相信如果没有任何组合,我们将无法构建非常复杂的示例。
最大的问题是:为什么 您会使用 profunctor 界面,而不是基于类别的界面? 必须不作文的问题有哪些?答案几乎就在 Star
的 Category
实例中:我不得不提出 Monad f
的繁重要求。这对于 profunctor 实例不是必需的:它们只需要 Functor f
。因此,对于 Star
作为强profunctor 的大多数重点示例,您需要查看 而不是 applicatives/monads 的基函子。此类函子相关的一个重要应用是 Van Laarhoven lenses, and the internal implementation of those give indeed probably the most insightful examples for strong profunctors. I keep getting dizzy whenever I go through the source of the lens library, but I think one instance that is quite impactful is Strong Indexed.
我正在浏览以下代码示例,发现很难弄清楚如何使用 (->) 和 (Star f) 一旦他们实现 'first' 并成为 Cartisian 的成员。
谁能提供一些易于理解的例子?谢谢
-- Intuitively a profunctor is cartesian if it can pass around additional
-- context in the form of a pair.
class Profunctor p => Cartesian p where
first :: p a b -> p (a, c) (b, c)
first = dimap swapP swapP . second
second :: p a b -> p (c, a) (c, b)
second = dimap swapP swapP . first
instance Cartesian (->) where
first :: (a -> b) -> (a, c) -> (b, c)
first f = f `cross` id
instance Functor f => Cartesian (Star f) where
first :: Star f a b -> Star f (a, c) (b, c)
first (Star f) = Star $ (\(fx, y) -> (, y) <$> fx) . (f `cross` id)
注意,意见提前!
Profunctors 有点过于抽象。在我看来,我们应该首先讨论 类别 ;实际上,大多数发音者都是类别,但反之则不然。 profunctor class 可能有有效的用途,但它实际上更加有限并且与 Hask 类别相关。我更喜欢通过讨论其箭头构造函数在最后一个参数中是 Hask-函子和在 pænultimate 参数中是逆变 Hask-函子的类别来明确这一点.是的,这有点啰嗦,但这就是重点:这实际上是一个非常具体的情况,而且通常情况下你真的只需要一个不太具体的类别。
具体来说,Cartesian
更自然地被视为 class 类别,而不是 profunctors:
class Category k => Cartesian k where
swap :: k (a,b) (b,a)
(***) :: k a b -> k a' b' -> k (a,a') (b,b')
允许
first :: Cartesian k => k a b -> k (a,c) (b,c)
first f = f *** id
second :: Cartesian k => k a b -> k (c,a) (c,b)
second f = id *** f
这就是 category-agnostic id
。 (您还可以根据 first
、second f=swap.first f.swap
和 f***g=first f.second g
来定义 ***
和 second
,但在我看来这是不雅的纠缠。)
为了了解为什么我更喜欢这种方式,而不是使用 profunctors,我想举一个简单的例子,不是 profunctor:线性映射。
newtype LinearMap v w = LinearMap {
runLinearMap :: v->w -- must be linear, i.e. if v and w are finite-dimensional
-- vector spaces, the function can be written as matrix application.
}
这 不是 profunctor:尽管您可以使用此特定实现编写 dimag f g (LinearMap a) = LinearMap $ dimap f g a
,但这不会保持线性。然而,它是一个笛卡尔范畴:
instance Category LinearMap where
id = LinearMap id
LinearMap f . LinearMap g = LinearMap $ f . g
instance Cartesian LinearMap where
swap = LinearMap swap
LinearMap f *** LinearMap g = LinearMap $ f *** g
好的,这看起来很微不足道。为什么这很有趣?好吧,线性映射可以有效地存储为矩阵,但从概念上讲,它们主要是 函数 。因此,像处理函数一样处理它们是有意义的;在这种情况下,.
有效地实现了矩阵乘法,***
以类型安全的方式将 block diagonal matrix 放在一起。
显然,您也可以使用不受限制的函数来完成所有这些操作,所以 instance Cartesian (->)
真的很简单。但是我给出了线性映射示例来激励 Cartesian
class 可以做一些没有必要的事情。
Star
才是真正有趣的地方。
newtype Star f d c = Star{runStar :: d->f c}
instance Monad f => Category (Star f) where
id = Star pure
Star f . Star g = Star $ \x -> f =<< g x
instance Monad f => Cartesian (Star f) where
swap = Star $ pure . swap
Star f *** Star g = Star $ \(a,b) -> liftA2 (,) (f a) (g b)
Star
是 kleisli category 的前身,您可能听说过它是使用单子计算链接的一种方法。那么让我们直接来看一个 IO
示例:
readFile' :: Star IO FilePath String
readFile' = Star readFile
writeFile' :: Star IO (FilePath,String) ()
writeFile' = Star $ uncurry writeFile
现在我可以做类似的事情了
copyTo :: Star IO (FilePath, FilePath) ()
copyTo = writeFile' . second readFile'
我为什么要这样做?关键是我已经将 IO 操作链接在一起,而没有使用可以通过任何方式 peek/modify 传递的 data 的接口。这对于安全应用程序可能很有趣。 (我刚刚编造了这个例子;我相信可以找到不那么做作的例子。)
无论如何,到目前为止我还没有真正回答这个问题,因为你不是在问笛卡尔范畴,而是在问 strong profunctors。不过,它们确实提供了几乎相同的界面:
class Profunctor p => Strong p where
first' :: p a b -> p (a, c) (b, c)
second' :: p a b -> p (c, a) (c, b)
因此我也可以做出微小的改变
copyTo :: Star IO (FilePath, FilePath) ()
copyTo = writeFile' . second' readFile'
保留基本相同的示例,但使用 Strong
而不是 Cartesian
。不过,我仍在使用 Category
组合。而且我相信如果没有任何组合,我们将无法构建非常复杂的示例。
最大的问题是:为什么 您会使用 profunctor 界面,而不是基于类别的界面? 必须不作文的问题有哪些?答案几乎就在 Star
的 Category
实例中:我不得不提出 Monad f
的繁重要求。这对于 profunctor 实例不是必需的:它们只需要 Functor f
。因此,对于 Star
作为强profunctor 的大多数重点示例,您需要查看 而不是 applicatives/monads 的基函子。此类函子相关的一个重要应用是 Van Laarhoven lenses, and the internal implementation of those give indeed probably the most insightful examples for strong profunctors. I keep getting dizzy whenever I go through the source of the lens library, but I think one instance that is quite impactful is Strong Indexed.