首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >笛卡尔(质子)的例子?

笛卡尔(质子)的例子?
EN

Stack Overflow用户
提问于 2018-12-14 10:27:13
回答 1查看 346关注 0票数 2

我正在研究下面的代码示例,发现一旦它们实现了“first”并成为Cartisian的成员,就很难弄清楚如何使用(->)和(Star )。

有人能提供一些容易理解的例子吗?谢谢。

代码语言:javascript
复制
-- 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)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-12-14 12:19:34

注意,前面有意见!

Pro函子是一个过度流行的抽象。我们应该先讨论范畴;实际上,大多数的程序都是范畴,反之亦然。profunctor类可能有有效的用途,但实际上它比Hask类别有更多的限制。我更喜欢通过讨论箭头构造函数在最后一个参数中是Hask-functors和p n终极参数中的反变体Hask-functors的类别来说明这一点。是的,这是一大口,但这就是重点:这实际上是一个非常具体的情况,而且通常情况下,你真的只需要一个不那么具体的类别。

具体来说,Cartesian更自然地被认为是一类范畴,而不是程序类型:

代码语言:javascript
复制
class Category k => Cartesian k where
  swap :: k (a,b) (b,a)
  (***) :: k a b -> k a' b' -> k (a,a') (b,b')

这允许

代码语言:javascript
复制
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

这就是id。(你也可以用***second来定义first,用second f=swap.first f.swapf***g=first f.second g来定义,但这是不恰当的纠缠。)

为了了解为什么我更喜欢这样做,而不是用终止函数,我想给出一个简单的例子,它不是一个函数:线性映射。

代码语言:javascript
复制
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.
 }

这是而不是,虽然您可以使用这个特定的实现编写dimag f g (LinearMap a) = LinearMap $ dimap f g a,但这不会保持线性。然而,这是一个笛卡尔的类别:

代码语言:javascript
复制
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

好吧,这看起来很琐碎。为什么这很有趣?嗯,线性映射可以有效地存储为矩阵,但从概念上讲,它们是最重要的函数。因此,处理它们类似于函数是有意义的;在这种情况下,.有效地实现了矩阵乘法,而***以一种类型安全的方式组合了一个块对角矩阵

显然,您也可以使用不受限制的函数来完成所有这一切,因此instance Cartesian (->)实际上是微不足道的。但是,我给出了一个线性映射示例,以激励Cartesian类能够完成一些没有它就没有必要的琐碎工作。

Star是它变得非常有趣的地方。

代码语言:javascript
复制
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)

Starkleisli范畴的前身,正如您可能听说的那样,这是使用一元计算链的一种方法。让我们直接来看一个IO示例:

代码语言:javascript
复制
readFile' :: Star IO FilePath String
readFile' = Star readFile

writeFile' :: Star IO (FilePath,String) ()
writeFile' = Star $ uncurry writeFile

现在我可以做这样的事

代码语言:javascript
复制
copyTo :: Star IO (FilePath, FilePath) ()
copyTo = writeFile' . second readFile'

我为什么要这样做?关键是,我将IO操作链接在一起,而没有使用接口来查看/修改传递的数据。对于安全应用程序来说,这可能很有趣。(我只是编了这个例子,我相信可以找到较少人为的例子。)

无论如何,到目前为止,我还没有真正回答这个问题,因为你问的不是笛卡尔类,而是强截子。不过,它们确实提供了几乎相同的界面:

代码语言:javascript
复制
class Profunctor p => Strong p where
  first' :: p a b -> p (a, c) (b, c)
  second' :: p a b -> p (c, a) (c, b)

所以我也可以做些小小的改变

代码语言:javascript
复制
copyTo :: Star IO (FilePath, FilePath) ()
copyTo = writeFile' . second' readFile'

在本质上保留相同的示例,但使用Strong而不是Cartesian。不过,我仍然在使用Category组合。我相信,如果没有任何构图,我们就无法建立非常复杂的例子。

大问题是:为什么要使用接口而不是基于类别的接口呢?没有作文就必须做什么问题?答案在很大程度上取决于Category实例Star:在那里,我必须对Monad f提出很高的要求。对于终止程序实例来说,这不是必需的:这些实例只需要Functor f。因此,对于大多数聚焦于Star作为一个强质子函数的例子,您将希望看到不是应用程序/单子的基函子。这类函子相关的一个重要应用是在Van 镜片中,而这些函子的内部实现可能确实给出了强凸函数的最有洞察力的例子。每当我穿过镜头库的源时,我就会头晕,但我认为有一个非常有影响的例子是强索引

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53777851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档