最近我幽默地注意到 liftA
可以写成
liftA (<*>) pure
我认为这很整洁,所以作为一个笑话,我想我会为
liftA
重新定义一个“定义”。基于此属性:f = f (<*>) pure
现在我预计这将是与
liftA
相同的类型。那从未停止过。但是它无法编译。• Occurs check: cannot construct the infinite type:
t ~ (f (a -> b) -> f a -> f b) -> (a1 -> f1 a1) -> t
• In the expression: f (<*>) pure
In an equation for ‘f’: f = f (<*>) pure
• Relevant bindings include
f :: (f (a -> b) -> f a -> f b) -> (a1 -> f1 a1) -> t
(bound at liftA.hs:2:1)
这似乎是明智的,我看到编译器有问题。但是事情变得有点奇怪,因为当我添加注释时:
f :: Applicative f => (a -> b) -> f a -> f b
f = f (<*>) pure
它突然编译。
现在我最初的怀疑是我注释的类型
f
with 不是最通用的类型,通过限制类型,我可以统一事物。但是,从类型来看,这似乎并非如此,我的类型似乎比编译器试图派生的类型更通用。这里发生了什么?我在这里有点超出我的深度,但我很好奇编译器在每种情况下的想法以及为什么它会遇到一个问题而不是另一个问题。
最佳答案
混淆是由 Haskell 的类型类和固定类型的函数是 Applicative
的一个实例造成的。 (又名读者单子(monad))。如果你用专门的版本写出来,它会变得更清楚:
type Reader a b = a -> b
fmapFn :: (a -> b) -> Reader c a -> Reader c b
fmapFn = fmap
-- ≡ liftA
-- ≡ (.)
fmap' :: Applicative f => (a -> b) -> f a -> f b
fmap' = fmapFn (<*>) pure
≡ (<*>) . pure
≡ \φ -> (<*>) (pure φ)
≡ \φ fa -> pure φ <*> fa
在这一点上,它需要适用法律
fmap f x = pure f <*> x
所以
fmap' ≡ \φ fa -> fmap φ fa
≡ fmap
呃。但关键是,在定义
fmap' = fmap' (<*>) pure
, (<*>)
和 pure
属于您希望它最终起作用的仿函数,但 fmap'
您使用的实际上始终属于函数仿函数。这在 Haskell 中没问题:毕竟定义是多态的,所以如果顶层知道如何为所有仿函数执行此操作,那么您当然也可以将它用于函数仿函数。 (抛开循环依赖导致的不终止问题不谈……)但是,因为您以
fmap' = ...
的形式定义它。 , monomorphism restriction开始:如果你写 fmap' = fmap' (<*>) pure
在顶层没有签名的情况下,编译器会尝试找到一个可以工作的具体类型,特别是单个具体的仿函数。但无论您选择哪种具体类型,这将是与 fmapFn
不同的类型。你正在尝试使用自己。因此,此定义仅使用强制其为多态的显式签名进行编译(或者,使用 -XNoMonomorphismRestriction
标志,这会导致编译器在没有显式指令的情况下选择多态类型)。编辑令人惊讶的是,它不是单态限制试图使类型减少不必要的多态性。为了弄清楚它是什么,让我们尝试找到一个具有相同问题的更简单的示例。第一次尝试:
fromFloat :: RealFrac a => Float -> a
toFloat :: RealFrac a => a -> Float
fromFloat = realToFrac
toFloat = realToFrac
s = fromFloat . s . toFloat
(我选择
Float
因为它不是编译器可能自行选择的 default
类型。)事实证明这编译得很好,但不是最通用的类型
s' :: (RealFrac a, RealFrac b) => a -> b
s' = fromFloat . s' . toFloat
它只是拿起更简单的
s :: Float -> Float
...无论是否启用了单态限制。为什么?我不知道;我会发现这是一个有趣的问题。
关于haskell - 解析 `f = f (<*>) pure`的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55388119/