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