简体   繁体   English

`Fix` 和 `(,)` 在某种意义上可以被视为函子吗?

[英]Can `Fix` and `(,)` be seen as functors in some sense?

I've been wondering what a complete, all-encompassing context for instance Functor (f:.: g) would be.我一直想知道什么是完整的、无所不包的上下文, instance Functor (f:.: g) The immediate thought that pops into my head is:我脑子里立刻冒出的想法是:

newtype (f :.: g) a = Comp (f (g a))
instance (Functor f, Functor g) => Functor (f :.: g) where
    fmap f (Comp x) = Comp (fmap (fmap f) x)

But then, two contravariant functors would also compose to be covariant, like so:但是,两个逆变函子也会组合成协变的,如下所示:

instance (Contravariant f, Contravariant g) => Functor (f :.: g) where
    fmap f (Comp x) = Comp (contramap (contramap f) x)

Already not a promising beginning.已经不是一个有希望的开始。 However, I've also noticed that technically, f and g don't even have to have kind * -> * -- the only requirement for f:.: g:: * -> * is that f:: k -> * and g:: * -> k for some k .但是,我也注意到,从技术上讲, fg甚至不必有种类* -> * —— f:.: g:: * -> *的唯一要求是f:: k -> *g:: * -> k对于一些k This means that non-functor types could compose to be functors, eg这意味着非函子类型可以组合成函子,例如

newtype Fix f = Fix (f (Fix f))
instance Functor (Fix :.: (,)) where
    fmap f (Comp x) = Comp (go x) where
        go (Fix (x,xs)) = Fix (f x,go xs)

Fix:.: (,) is isomorphic to the Stream type: Fix:.: (,)同构于Stream类型:

data Stream a = a :> Stream a

so this does seem to be a non-trivial issue.所以这似乎是一个不平凡的问题。 This got me thinking -- if Haskell's Functor typeclass represents categorical functors from Hask to Hask, does that mean types like Fix and (,) could be functors working on some other categories?这让我开始思考——如果 Haskell 的Functor类型类代表从 Hask 到 Hask 的分类函子,这是否意味着像Fix(,)这样的类型可能是处理其他类别的函子? What would those categories be?这些类别是什么?

Yes, and we can read off exactly what sense that is from the shape of the constructor.是的,我们可以从构造函数的形状中准确地读出它的含义。 Let's look at (,) first.我们先看(,)

The (,) Functor (,)函子

(,) :: * -> * -> *

This takes two types and produces a type.这需要两种类型并产生一种类型。 Up to isomorphism, this is equivalent to直到同构,这等同于

(,) :: (*, *) -> *

ie we might as well uncurry the function and take both arguments at once.也就是说,我们不妨取消 function 并同时获取两个 arguments。 So (,) can be viewed as a functor for Hask × Hask to Hask , where Hask × Hask is the product category .所以(,)可以看作是Hask × HaskHask的函子,其中Hask × Hask产品类别 We have a word for a functor whose domain is the product of two categories.我们有一个词来表示定义域是两个范畴的乘积的函子。 We call it a bifunctor , and it's actually in base Haskell .我们称它为双函子,它实际上位于base Haskell 中 Specifically, a bifunctor p is capable of turning maps from (a, b) to (a', b') in the product category into maps from pab to pa' b' .具体来说,双函子p能够将产品类别中从(a, b)(a', b')的映射转换为从pabpa' b'映射。 Haskell's typeclass writes this in a slightly different but equivalent way Haskell 的类型类以一种略有不同但等效的方式编写它

bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d

Having a map a -> b and a map c -> d is exactly equivalent to having a map (a, c) -> (b, d) in the product category .拥有 map a -> b和 map c -> d完全等同于拥有 map (a, c) -> (b, d)在产品类别中 (What I mean by that is: the maps (a, c) -> (b, d) in the product category are defined to be products of maps a -> b and c -> d ). (我的意思是:产品类别中的地图(a, c) -> (b, d)定义为地图a -> bc -> d的产品)。

The Fix Functor Fix函子

We can deal with Fix the same way.我们可以用同样的方式来处理Fix

newtype Fix f = Fix (f (Fix f))

Its shape is它的形状是

Fix :: (* -> *) -> *

It takes a one-argument type constructor and produces a type.它采用单参数类型构造函数并生成一个类型。

Now, in Haskell, the * -> * part can be any one-argument type constructor, but categorically it's much nicer to work with functors.现在,在 Haskell 中, * -> *部分可以是任何单参数类型的构造函数,但绝对可以更好地使用仿函数。 So I'm going to make the slightly stronger constraint (which it turns out we'll need in a minute) that the * -> * argument to Fix is a Functor , ie a functor from Hask to Hask .所以我要稍微加强约束(事实证明我们很快就会需要),即Fix* -> *参数是一个Functor ,即从HaskHask的函子。

In that case, Fix has the right shape to be a functor from the functor category Hask ^ Hask to the category Hask .在这种情况下, Fix具有正确的形状,可以成为从函子类别Hask ^ Hask到类别Hask的函子。 A functor, categorically, takes objects to objects and arrows to arrows.明确地说,函子将对象带到对象,将箭头带到箭头。 So let's take that one step at a time.因此,让我们一次迈出这一步。

The object part is easy, we've already defined it. object 部分很简单,我们已经定义过了。 Specifically, Fix takes the functor f (functors are the objects of a functor category; read that again if it doesn't make sense yet) and maps it to the type Fix f that we just defined.具体来说, Fix获取函子f (函子是函子类别的对象;如果还不明白请再读一遍)并将其映射到我们刚刚定义的类型Fix f

Now, the arrows of a functor category are natural transformations .现在,函子范畴的箭头自然变换 Given two functors f, g:: C -> D , a natural transformation α from f to g is a mapping from the objects of C to the arrows of D .给定两个函子f, g:: C -> D ,从fg的自然变换α是从C对象D箭头的映射。 Specifically, for every object x in the category C , α x should be an arrow in D going from fx to gx , with the following coherence condition:具体来说,对于类别C中的每个 object xα x应该是D中从fxgx的箭头,具有以下一致性条件:

For every arrow h: x -> y in C , we must have (gh). (α x) === (α y). (fh)对于C中的每个箭头h: x -> y ,我们必须有(gh). (α x) === (α y). (fh) (gh). (α x) === (α y). (fh)

(Using notation such as function composition very loosely, in true category theory spirit) (使用 function 等符号非常松散,本着真正的范畴论精神)

Drawn as a commutative diagram, the following must commute,绘制为交换图,以下必须交换,

表示 (g h) 的交换图。 (α x) === (α y) 。 (女)

Haskell doesn't really have a built-in type for natural transformations. Haskell 实际上并没有用于自然转换的内置类型。 With Rank-N types , we can write the correct shape使用Rank-N types ,我们可以写出正确的形状

(forall a. f a -> g a)

This is the shape of a natural transformation, but of course we haven't verified the coherence property.这是一个自然变换的形状,当然我们还没有验证相干性。 So we'll just have to trust that it satisfies that property.所以我们只需要相信它满足那个属性。

With all of this abstract nonsense in mind, if Fix is going to be a functor from Hask ^ Hask to Hask , it should take a natural transformation to an ordinary Haskell function, and it should have the following shape.考虑到所有这些抽象的废话,如果Fix将成为从Hask ^ HaskHask的函子,它应该自然转换为普通的 Haskell function,并且它应该具有以下形状。

fixmap :: (Functor f, Functor g) => (forall a. f a -> g a) -> Fix f -> Fix g

Once we have this type, we can write the implementation fairly easily.一旦我们有了这个类型,我们就可以很容易地编写实现。

fixmap h (Fix inner) = Fix (h . fmap (fixmap h) $ inner)

or, equivalently (by the rules of natural transformations),或者,等价地(根据自然变换的规则),

fixmap h (Fix inner) = Fix (fmap (fixmap h) . h $ inner)

I'm not aware of an idiomatic name for this shape of functor, nor am I aware of a typeclass that encompasses it, but of course nothing stops you from making it yourself.我不知道这种仿函数的惯用名称,也不知道包含它的类型类,但当然没有什么能阻止你自己制作它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM