从范畴论的角度来看,函子是一对映射(一个在对象之间,另一个在范畴的箭头之间),遵循一些公理。
我假设每个函子实例都类似于数学定义,即可以映射对象和函数,但Haskell的Functor
类只有将函数映射的fmap
函数。
为什么呢?
更新:换句话说:
每种Monad类型M
都有一个函数return :: a -> M a
。
而Functor类型F
没有函数return :: a -> F a
,只有F x
构造函数。
从范畴论的角度来看,函子是一对映射(一个在对象之间,另一个在范畴的箭头之间),遵循一些公理。
我假设每个函子实例都类似于数学定义,即可以映射对象和函数,但Haskell的Functor
类只有将函数映射的fmap
函数。
为什么呢?
更新:换句话说:
每种Monad类型M
都有一个函数return :: a -> M a
。
而Functor类型F
没有函数return :: a -> F a
,只有F x
构造函数。
* -> *
种类:
α -> F α
(对于Functor F
),β -> M β
(对于Monad M
)。fmap :: (α -> β) -> (F α -> F β)
重要的是,Monad
的 return :: α -> M α
不是你可能认为的类型 α
到 M α
的映射器。根据单子的数学定义,return
对应于从 Id
函子到 M
函子的自然变换。而且在 Hask 中,Id
函子有点隐含。单子的标准定义还需要另一个自然变换 M ◦ M -> M
。因此,在 Haskell 中进行翻译如下:
class Functor m => Monad m where
return :: Id α -> m α
join :: m (m α) -> m α
>>= :: m α -> (α -> m β) -> m β
派生的类似于组合的运算符:(>=>) :: Monad m => (α -> m β) -> (β -> m γ) -> (α -> m γ)
f >=> g = \a => f a >>= g
一个范畴的对象与面向对象(OO)编程语言中的对象不同(在Haskell中我们更倾向于将它们称为值;关于它们在范畴论中的含义,可以参考这里)。相反,在Hask中的对象是类型。Functor
在Hask中是自函子(endofunctors),即通过以下方式将类型与类型关联:
Prelude> :k Maybe
Maybe :: * -> *
Prelude> :k Int
Int :: *
Prelude> :k Maybe Int
Maybe Int :: *
另一方面,Hask中的箭头实际上是一些函数类型a -> b
的值。它们是以下方式关联的:
fmap :: ( Functor (f :: t -> f t {- type-level -} ) )
=> (a->b) -> fmap(a->b) {- value-level -}
≡ (a->b) -> (f a->f b)
instance Functor [] where
fmap f (x : xs) = f x : fmap xs
fmap _ [] = []
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap _ Nothing = Nothing
instance Functor ((,) a) where
fmap f (x, y) = (x, f y)
return
怎么处理呢?instance Functor [] where
return x = [x]
instance Functor Maybe where
return x = Just x
instance Functor ((,) a) where
return x = (??? , x)
a
,但我们并不知道这个类型中的值。也许类型a
是只有一个值的Unit
类型。但如果它是Bool
类型,我们应该选择True
还是False
?如果它是Either Int Bool
类型,我们应该选择Left 0
、Right False
还是Left 1
?return
语句,通常情况下就不能定义很多有效的functor实例(你需要强制使用类似于FunctorEmpty的类型类约束)。Functor
和Monad
的文档,您会发现确实存在Functor ((,) a)
的实例,但不存在Monad ((,) a)
的实例。这是因为您无法为该实例定义return
。instance Functor F where
fmap = ...
那么类型构造函数 F
是作用于对象(即类型)的操作,它将类型 T
映射到类型 F T
,而 fmap
是作用于态射(即函数)的操作,它将函数 f :: T -> U
映射到 fmap f :: F T -> F U
。
Functor
和Monad
类的情况完全相反”是什么意思?由于单子确实是函子,所以它不能相反。至于“为什么范畴论论证适用于Haskell类型理论”:类型理论与此毫无关系。这只是_Haskell标准库_,它们实现了模拟范畴论概念的类型类。 - leftaroundaboutreturn
代表自然变换 _η_:1 → _T_,它适用于每个单子但不适用于一般的函子。那么...你还有什么疑问吗? - leftaroundabout