两个仿函数的组成是一个仿函数

时间:2012-12-23 21:50:02

标签: haskell functor category-theory

a previous answer中,Petr Pudlak定义了CFunctor类,用于除 Hask Hask 之外的算子。使用类型系列重写它,它看起来像

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 所有这一切的最终原因是还引入了一个包含方法Adjunctionunit的类counit,然后从附件中自动派生monad实例。但首先,我需要向编译器显示两个仿函数的组合也是一个仿函数。

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

1 个答案:

答案 0 :(得分:6)

给定仿函数F : C → DG : D → E,仿函数合成G ∘ F : C → E是类别CE之间的对象映射,这样{{1}和morphisms之间的映射,使(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

我们可以从Dom f a b -> Cod g (Wrap g f a) (Wrap g f b)g (f a),反之亦然,但由于实例声明不对Wrap g f的结构做出任何假设,我们运气不佳。 / p>

由于我们知道functor是类别之间的映射,我们可以使用Cod gCod g的事实(在Haskell方面,这需要Category约束),这给出了我们很少有与之合作的行动:

Category (Cod g)

但是,这需要一个方便的提升操作员cmap f = lift? unWrap >>> cmap (cmap f) >>> lift? Wrap ,它将功能从lift?类别提升到Hask类别。将Cod g写为Cod g(~>)的类型必须为:

lift?

现在,这个提升算子至少有两种选择:

  • 您可以将constrait从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)
  • 或者,正如Sjoer Visscher在评论中提到的那样,您可以在运行时使用arrWrap在语义上unWrap的事实,在这种情况下使用id是有道理的。