haskell - 解析 `f = f (<*>) pure`的类型

标签 haskell types typechecking applicative

最近我幽默地注意到 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/

相关文章:

haskell 错误: cannot derive well-kinded instance/kind-mismatch

haskell - 将有效的 forall 类型归因于 let-bound 变量时出现类型错误

haskell - 重叠实例有什么不好?

haskell - 为什么这些 Haskell Parsec 组合器的顺序很重要?

java - 强制子类提供初始化方法

typescript 使用条件类型推断构造函数参数

haskell - Lambdabot `sourcePlugin' 失败,返回 : state not initialized

Haskell计算函数执行时间

scala - 当参数不符合类型约束时,为什么这个 Scala 函数会编译?

python-3.x - 为什么 type(mock.MagicMock()) == mock.MagicMock 返回 False?