简体   繁体   English

解决`f = f(<*>)pure`的类型

[英]Resolving the type of `f = f (<*>) pure`

Recently I noticed that humourously liftA can be written as 最近我注意到,幽默的liftA可以写成

liftA (<*>) pure

I thought this was neat and so as a bit of a joke I thought I would make a new "definition" of liftA based on this property: 我认为这很整洁,所以作为一个笑话,我想我会根据这个属性制作一个新的“定义” liftA

f = f (<*>) pure

Now I had expected that this would be something of the same type as liftA that just never halted. 现在我曾预料到这将是与从未停止过的liftA相同的类型。 However it fails to compile. 但它无法编译。

• 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)

This seems sensible, I see how the compiler has an issue. 这似乎是明智的,我看到编译器有一个问题。 However things get a little weird because when I add an annotation: 然而,当我添加注释时,事情变得有点奇怪:

f :: Applicative f => (a -> b) -> f a -> f b
f = f (<*>) pure

It suddenly compiles. 它突然编译。

Now my initial suspicion was that the type I was annotating f with was not the most general type and that by restricting the type I had made it possible to unify things. 现在我的最初的怀疑是,我是注释类型f与不是最普遍的类型,并且通过限制型我曾使我们能够以统一的事情。 However looking at the types this doesn't seem to be the case my type seems to be more general than the type that the compiler was trying to derive. 然而,看看类型似乎并非如此,我的类型似乎比编译器试图派生的类型更通用。

What is going on here? 这里发生了什么? I am a bit out of my depth here but I am curious as to what the compiler is thinking in each scenario and why it encounters an issue in one but not the other. 我有点超出了我的深度,但我很好奇编译器在每个场景中的想法以及为什么它遇到一个问题而不是另一个问题。

The confusion is caused by Haskell's type classes and the fact that functions-from-fixed-type are an instance of Applicative (aka the reader monad). 混淆是由Haskell的类型类引起的,并且函数from-fixed-type是Applicative的实例(也就是读者monad)。 It becomes clearer if you write it out with specialised version: 如果你用专门的版本写出它会变得更清楚:

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

And at this point it requires the applicative law 在这一点上,它需要适用法律

fmap f x = pure f <*> x

so 所以

 fmap' ≡ \φ fa -> fmap φ fa
       ≡ fmap

duh . But the point is, in the definition fmap' = fmap' (<*>) pure , the (<*>) and pure belong to the functor for which you want this to eventually work, but the fmap' you're using actually always belongs to the function functor. 但关键是,在定义fmap' = fmap' (<*>) pure(<*>)pure属于你想让它最终工作的fmap' ,但是你实际使用fmap'总是属于函数functor。 That's ok in Haskell: the definition is polymorphic after all, so if the top-level knows how to do this for all functors then you can certainly also use it for the function functor. 在Haskell中没问题:毕竟定义多态的,所以如果顶级知道如何为所有仿函数执行此操作,那么您当然也可以将它用于函数仿函数。 (Leaving aside the issue of nontermination due to circular dependency...) However, because you're defining it in the form fmap' = ... , (不考虑由于循环依赖导致的fmap' = ...问题......)但是,因为你以fmap' = ...的形式定义它, the monomorphism restriction kicks in: if you write fmap' = fmap' (<*>) pure without signature at the top-level, 单态限制启动:如果你在顶级写入fmap' = fmap' (<*>) pure而没有签名, the compiler tries to find a concrete type for which this should work, in particular a single, concrete functor. 编译器试图找到一个应该适用的具体类型,特别是一个具体的仿函数。 But whatever concrete type you choose, this will then be a different type from the fmapFn that you're trying to use yourself. 但是无论你选择哪种具体类型,这都将是你自己尝试使用的fmapFn的不同类型。 So, this definition only compiles with an explicit signature that forces it to be polymorphic 因此,此定义仅使用显式签名进行编译,以强制它为多态 (or alternatively, with the -XNoMonomorphismRestriction flag, which causes the compiler to pick the polymorphic type without explicit instruction) (或者,使用-XNoMonomorphismRestriction标志,这会导致编译器在没有显式指令的情况下选择多态类型) .

EDIT Surprisingly it turns out it is not the monomorphism restriction what tries to make the type less polymorphic than necessary. 编辑令人惊讶的是,事实证明, 并不是单形态限制试图使类型不必要的多态性。 To figure out what it is, let's try to find a simpler example with the same problem. 为了弄清楚它是什么,让我们试着找到一个带有同样问题的简单例子。 First attempt: 第一次尝试:

fromFloat :: RealFrac a => Float -> a
toFloat :: RealFrac a => a -> Float
fromFloat = realToFrac
toFloat   = realToFrac

s = fromFloat . s . toFloat

(I chose Float because it's not a default type that the compiler might pick by itself.) (我选择了Float因为它不是编译器可能自己选择的default类型。)
Turns out this compiles just fine, but instead of the most general type 原来这个编译得很好,但不是最常见的类型

s' :: (RealFrac a, RealFrac b) => a -> b
s' = fromFloat . s' . toFloat

it just picks up the simpler 它只是更简单

s :: Float -> Float

...regardless of whether the monomorphism restriction is enabled. ...无论是否启用单态限制。 Why? 为什么? I don't know; 我不知道; I'd find this an interesting question to ask. 我觉得这个问题很有意思。

It's because the f used in the body of the definition of f has a different type than the definition. 这是因为在f的定义体中使用的f具有与定义不同的类型。 This is called polymorphic recursion, and Haskell only allows that if you provide a type signature. 这称为多态递归,而Haskell只允许在提供类型签名时使用。 The reason for requiring a type signature is that type inference for polymorphic recursion is undecidable in the general case. 需要类型签名的原因是,在一般情况下,多态递归的类型推断是不可判定的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM