我想我在hackage 文章中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
实例:
作为这些定律的结果,
Functor
f 的实例将满足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
or )而改变感兴趣。OverlappingInstances