10

之前的回答中,Petr PudlakCFunctor为除从HaskHask的函子之外的函子定义了该类。使用类型族重新编写它,它看起来像

class CFunctor f where
  type Dom f :: * -> * -> *               -- domain category
  type Cod f :: * -> * -> *               -- codomain category
  cmap :: Dom f a b -> Cod f (f a) (f b)  -- map morphisms across categories

看起来像的实例,例如

instance CFunctor Maybe where
  type Dom Maybe = (->)                   -- domain is Hask
  type Cod Maybe = (->)                   -- codomain is also Hask 
  cmap f = \m -> case m of
                   Nothing -> Nothing
                   Just x  -> Just (f x)

在范畴论中,只要F : C --> D是函子且G : D --> E是函子,则组合GF : C --> E也是函子。

我想用 Haskell 来表达这一点。因为我不会写instance CFunctor (f . g),所以我介绍一个包装类:

newtype Wrap g f a = Wrap { unWrap :: g (f a) }

在写这个CFunctor实例时,我得到了

instance (CFunctor f, CFunctor g, Cod f ~ Dom g) => CFunctor (Wrap g f) where
  type Dom (Wrap g f) = Dom f
  type Cod (Wrap g f) = Cod g
  cmap = undefined

但我无法弄清楚cmap应该是什么。有什么建议吗?

PS这一切的最终原因是还引入了一个Adjunction带有方法的类unitand counit,然后自动从附属词中派生monad实例。但首先,我需要向编译器展示两个仿函数的组合也是一个仿函数。

我知道我可以cmap.cmap在类型的对象上使用g (f a)它并且会起作用,但这看起来有点像作弊 - 当然函子只是一个函子,编译器不必知道它实际上是两个的组合其他函子?

4

1 回答 1

6

给定函子F : C → Dand G : D → E,函子组合G ∘ F : C → E是类别Cand之间的对象的映射E,这样(G ∘ F)(X) = G(F(X))以及态射之间的映射,这样(G ∘ F)(f) = G(F(f))

这表明您的CFunctor实例应定义如下:

instance (CFunctor f, CFunctor g, Cod f ~ Dom g) => CFunctor (Wrap g f) where
  type Dom (Wrap g f) = Dom f
  type Cod (Wrap g f) = Cod g
  cmap f = cmap (cmap f)

然而,组合cmap两次给你Dom f a b -> Cod g (g (f a)) (g (f b))cmap在这种情况下有一个 type Dom f a b -> Cod g (Wrap g f a) (Wrap g f b)

我们可以从g (f a)to得到,Wrap g f反之亦然,但是由于实例声明没有对 的结构做出任何假设Cod g,所以我们不走运。

因为我们知道函子是类别之间的映射,我们可以使用事实Cod g是 a Category(在 Haskell 方面,这需要一个Category (Cod g)约束),这给了我们很少的操作:

cmap f = lift? unWrap >>> cmap (cmap f) >>> lift? Wrap

然而,这需要一个方便的提升操作符lift?,它将函数从一个Hask类别提升到另一个Cod g类别。写Cod g(~>),类型lift?必须是:

lift? :: (a -> b) -> (a ~> b)

lift? unWrap  :: Wrap g f a ~> g (f a)
cmap (cmap f) :: g (f a)    ~> g (f b)
lift? Wrap    :: g (f b)    ~> Wrap g f b

lift? unWrap >>> cmap (cmap f) >>> lift? Wrap :: Wrap g f a ~> Wrap g f b

现在,这个起重操作员至少有两种选择:

  • 您可以将约束从 扩大Category (Cod g)Arrow (Cod g)在这种情况下,起重操作员变为arr
  • Wrap或者,正如 Sjoerd Visscher 在评论中提到的那样,您可以在运行时使用andunWrap在语义上是这样的事实id,在这种情况下使用 ofunsafeCoerce是合理的。
于 2012-12-24T00:10:25.493 回答