我想我在the hackage article for Control.Applicative
中发现了一个缺陷。
作为应用仿函数法的描述,它说:
class Functor f => Applicative f where
带有应用程序的仿函数,提供操作 嵌入纯表达式(
pure
)和序列计算并组合它们的结果(<*>
)。最小完整定义必须包括满足以下法则的这些函数的实现:
身份
pure id <*> v = v
组合物
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
同态
pure f <*> pure x = pure (f x)
交换
u <*> pure y = pure ($ y) <*> u
(请注意,这并未说明有关fmap的任何内容)并且它声明这些法律确定了Functor
实例:
作为这些法律的结果,f的
Functor
实例将满足fmap f x = pure f <*> x
我一开始觉得这显然是错的。也就是说,我猜测必须存在满足以下两个条件的类型构造函数t
:
t
是Applicative
符合上述规则的实例,instance (Functor t)
有两种不同的实现方式
(即有两个不同的函数fmap1, fmap2 :: (a->b) -> (t a->t b)
,
满足算子法则。)如果(且仅当)上述内容正确无误,则必须 将上述语句重写为
f的Functor实例必须满足
fmap f x = pure f <*> x
由于这些法律,这符合
Functor
法律。
这显然是正确的,无论我的猜测是否正确。
我的问题是 :我的猜测正确吗?是否有t
具有所需条件?
以下是我自己在尝试回答这个问题时的想法。
如果我们仅仅是数学家对实际的Haskell编程不感兴趣,我们可以轻松肯定地回答这个问题。事实上,
t = Identity
newtype Identity a = Identity {runIdentity :: a}
符合上述要求1和2(事实上,几乎所有事情都可以)。实际上,Identity
只是Functor
和Applicative
的一个实例,如Data.Functor.Identity
中所定义。 (这满足fmap f = (pure f <*>)
。)
要定义instance (Functor f)
的另一个“实现”,请为每种类型a
选择两个函数
transf_a, tinv_a :: a -> a
这样
tinv_a . transf_a = id
(这是理论上设置,很简单)。现在定义
instance (Functor Identity) where
fmap (f :: a->b) = Identity . transf_b . f . tinv_a . runIdentity
这符合Functor
法律,如果有
x :: a
f :: a -> b
这样
f x /= transf_b $ f $ tinv_a x
但我们是否可以在Haskell中完成这一点并不明显。是这样的:
class (Isom a) where
transf, tinv :: a -> a
instance (Isom a) where
transf = id
tinv = id
specialized instance (Isom Bool) where
transf = not
tinv = not
可能在Haskell?
我忘了写一些非常重要的东西。我认为Control.Applicative
是GHC基础包的一部分,所以我也对我的问题的答案是否会因任何GHC语言扩展而改变感兴趣,例如FlexibleInstances
或OverlappingInstances
还不明白。
答案 0 :(得分:13)
Haskell中的任何类型最多只能有Functor
的一个实例,因此您的猜测不正确:对于没有类型t
,可以存在两种不同的instance (Functor t)
实现{1}}。请参阅:http://article.gmane.org/gmane.comp.lang.haskell.libraries/15384
答案 1 :(得分:2)
a -> a
类型的属性只有两个居民,即id :: a -> a
(定义为id x = x
)和bottom :: a -> a
定义为{{1} }}。
如果我们仅将第一种情况局限于“合理”,我们得出一个重要的数学定理,即至少有一个bottom = error "Infinite loop."
类型的函数fmap
满足{{1} }和forall a. forall b. (a -> b) -> f a -> f b
。
如果我们不这样做,那么你是对的,我们有一个案例fmap id = id
而另一个案例fmap f . fmap g = fmap (f . g)
。但那有点俗气。