Haskell 两个函子的组合就是一个函子
在中,Petr Pudlak为Hask到Hask之间的函子以外的函子定义了Haskell 两个函子的组合就是一个函子,haskell,functor,category-theory,Haskell,Functor,Category Theory,在中,Petr Pudlak为Hask到Hask之间的函子以外的函子定义了CFunctor类。使用类型族重新编写它,看起来 class CFunctor f where type Dom f :: * -> * -> * -- domain category type Cod f :: * -> * -> * -- codomain category cmap :: Dom f a b -> Cod
CFunctor
类。使用类型族重新编写它,看起来
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也是函子
我想用哈斯克尔来表达这一点。由于我不能编写实例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所有这一切的最终原因是还引入了一个带有方法unit
和condit
的类附加
,然后从附加自动派生monad实例。但首先,我需要向编译器展示两个函子的组合也是一个函子
我知道我可以在类型为
g(fa)
的对象上使用cmap.cmap
,但这似乎有点像作弊——当然一个函子只是一个函子,编译器不必知道它实际上是另外两个函子的组合?给定函子f:C→ D
和G:D→ E
,一个函子组合G∘ F:C→ E
是类别C
和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
将为您提供domfab->codg(g(fa))(g(fb))
并且cmap
在这种情况下具有类型domfab->codg(wrappgfa)(wrappgfb)
我们可以从g(f a)
到Wrap g f
,反之亦然,但是由于实例声明没有对Cod g
的结构做出任何假设,我们就不走运了
因为我们知道函子是类别之间的映射,所以我们可以利用Cod g
是类别
(在Haskell方面,这需要Category(Cod g)
约束),这给了我们一些操作:
cmap f = lift? unWrap >>> cmap (cmap f) >>> lift? Wrap
然而,这需要一个方便的提升操作员lift?
,将功能从Hask
类别提升到Cod g
类别。将Cod g
写入(~>)
,则升降机的类型必须为:
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
现在,该提升操作员至少有两种选择:
- 您可以将构造从
类别(Cod g)
扩展到箭头(Cod g)
,在这种情况下,起重操作员变成arr
- 或者,正如Sjoerd Visscher在评论中提到的那样,您可以使用以下事实:
Wrap
和unWrap
在运行时在语义上是id
,在这种情况下,使用unsafeceorce
是合理的
对于Hask上的内函子,DeriveFunctor
扩展肯定会自动处理(以及许多其他情况)。但是如果你必须手动实现它,我不明白为什么你会期望通过在每个函子上合成映射而在合成函子上映射更简单?基本上,cmap f=cmap(cmap f)
是函子合成的定义,那么,为什么要用它来作弊呢?@C.A.McCann我很乐意写cmap=cmap。cmap
作为实现,但对于类型构造函数Wrap g f
(特别是,它需要domfa b
到Cod g(g(f a))(g(f b))
而不是Cod g(Wrap g f a)(Wrap g f b)
。如上所述,我对Hask上的内函子以外的情况特别感兴趣。@DanielFischer因为g(f a)
不仅仅是两个函子的组合,它本身也是一个函子,所以我想我应该能够在它上面使用cmap
(类似于使用monad transformers时不必到处写lift.lift.lift
,如果你能告诉编译器如何自动提升某些函数——这里我想告诉编译器如何使用cmap
的单个应用程序跨两个类别映射态射)。我的意思是在cmap
的定义中使用它来定义Wrap f g
。但是,看看它,我不知道如何对一般g
这样做;只有Cod g=(>)
是显而易见的。我怀疑在存在类型族的情况下,unsafecterce
可能是一个不好的选择。无可否认,我还没有弄清楚任何细节。