从分类的角度来看,仿函数是一对两个映射(一个在对象之间,另一个在类别的箭头之间),遵循一些公理。
我假设,每个Functor实例与数学定义类似,即可以映射对象和函数,但Haskell的Functor
类只有映射函数的函数fmap
。
为什么这样?
UPD换句话说:
每个Monad类型M
都有return :: a -> M a
功能。
和Functor类型F
没有功能return :: a -> F a
,但只有F x
构造函数。
首先,有两个层次:类型和价值。由于Hask的对象是类型,您只能使用类型构造函数映射它们,类型构造函数具有* -> *
类型:
α -> F α
(Functor F
),β -> M β
(为Monad M
)。然后对于仿函数,你需要一个关于态射的映射(即函数,它们是值):它只是fmap :: (α -> β) -> (F α -> F β)
。
到目前为止,我想,我不是说任何新的东西。但重要的是return :: α -> M α
的Monad
不是你可能认为的α
类型M α
的映射器。关于monad的数学定义,return
对应于从Id
仿函数到M
仿函数的自然变换。只是这个Id
仿函数是隐含的。 monad的标准定义还需要另一种自然变换M ◦ M -> M
。所以将它翻译成Haskell就好了
class Functor m => Monad m where
return :: Id α -> m α
join :: m (m α) -> m α
(作为旁注:这两个自然变换实际上是单位和乘法,这使monad成为endofunctors类别中的monoid)
实际定义不同但是相同。请参阅Haskell/wiki。
如果你采用类似于组合的运算符派生自标准绑定qazxsw poi:
>>= :: m α -> (α -> m β) -> m β
你可以看到,它实际上是关于(>=>) :: Monad m => (α -> m β) -> (β -> m γ) -> (α -> m γ)
f >=> g = \a => f a >>= g
。另见Kleisli category关于计算机科学中的monads。
类别的对象与OO编程语言中的对象不同(我们更喜欢在Haskell中调用这些值;它们在类别理论中的含义是讨论过article on nLab)。相反,Hask的对象是类型。 Haskell heres是Hask中的endofunctors,即通过以下方式将类型关联到类型:
前奏>:k也许吧 也许:: * - > * 前奏>:k Int Int :: * 前奏>:k也许Int 也许Int :: *
OTOH,Hask的箭头实际上是某些函数类型Functor
的值。这些通过以下方式关联:
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 F where
fmap = ...
是对象(它是类型)的动作,将类型F
带到T
类型,而F T
是对函数fmap
到f :: T -> U
的态射(函数)的动作。
虽然你在你的问题中使用那些花哨的分类术语,并且应该对现有的答案完全满意,但这是尝试一个相当简单的解释:
假设在Functor类型类中会有一个函数fmap f :: F T -> F U
(或return
或pure
或unit
)。
现在尝试定义Functor的一些常见实例:...
(列表),[]
,Maybe
(带左侧组件的元组)
很容易,嗯?
以下是普通的Functor实例:
((,) a)
那么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)
for Functor怎么样?
列表:
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
?
所以你看,如果你在Functors上有一个Left 1
,你就无法定义很多有效的函子实例(你需要强加一个像FunctorEmpty类型类的约束)。
如果你查看return
和Functor
的文档,你会发现确实有Monad
的实例,但没有Functor ((,) a)
的实例。这是因为你无法为那件事定义Monad ((,) a)
。