简体   繁体   English

为什么这个eta扩展是必要的?

[英]Why is this eta expansion necessary?

Can someone help me understand this/point me to some reading materials? 有人可以帮我理解这个/给我一些阅读材料吗? The following works fine: 以下工作正常:

type F a b = Functor f => f a -> f b
fComp :: F b c -> F a b -> F a c
fComp f f' = f . f'

But if I write fComp = (.) instead, the type checker complains: 但是如果我改写fComp = (.) ,那么类型检查器会抱怨:

Couldn't match type ‘b0 -> f c’
              with ‘forall (f1 :: * -> *). Functor f1 => f1 b -> f1 c’

(This specific example isn't particularly useful; I'm just trying to shrink an example that came up while studying lenses.) (这个具体的例子并不是特别有用;我只是想缩小研究镜头时出现的例子。)

fComp has a higher-rank type and type inference for higher-rank types is very limited. fComp具有更高级别的类型和更高级别类型的类型推断是非常有限的。 It might be a little easier to understand (but much longer!) if we expand the type synonym: 如果我们扩展类型同义词,它可能会更容易理解(但更长!)

fComp :: forall f a b c. Functor f => 
                (forall f1. Functor f1 => f1 b -> f1 c) ->
                (forall f2. Functor f2 => f2 a -> f2 b) ->
                (f a -> f c)

The higher-rank types of f and f' are specified explicitly in this type signature. 在此类型签名中明确指定了较高等级的ff'类型。 This lets the type inference start already knowing the types of f and f' and so being able to unify them with the type of . 这使得类型推断开始已经知道ff'的类型,因此能够将它们与类型统一起来. .

However, if you get rid of f and f' , the type that . 但是,如果你摆脱ff' ,那就是那种类型. has to take is not known. 必须采取不知道。 Unfortunately, the system can't infer higher-rank types like this, so you get a type error. 不幸的是,系统无法推断出更高级别的类型,因此您会遇到类型错误。

In essence, the compiler can't create higher-rank types to fill unknowns during type inference and has to rely on programmer annotations and we need both the name ( f and f' ) and the type signature to get those annotations. 本质上,编译器不能创建更高级别的类型来填充类型推断期间的未知数,并且必须依赖于程序员注释,我们需要名称( ff'类型签名来获取这些注释。

An easier example to understand would be a higher-rank id function: 一个更容易理解的例子是更高级别的id函数:

myId :: (forall a. a) -> (forall b. b)

The definition myId x = id x compiles, but myId = id gives the following error: 定义myId x = id x编译,但myId = id给出以下错误:

/home/tikhon/Documents/so/eta-expansion-needed.hs:11:8:
    Couldn't match type ‘b’ with ‘forall a. a’
      ‘b’ is a rigid type variable bound by
          the type signature for myId :: (forall a. a) -> b
          at /home/tikhon/Documents/so/eta-expansion-needed.hs:11:1
    Expected type: (forall a. a) -> b
      Actual type: b -> b
    In the expression: id
    In an equation for ‘myId’: myId = id
Failed, modules loaded: none.

(Keep in mind that forall b. (forall a. a) -> b is the same as (forall a. a) -> (forall b. b) .) (请记住, forall b. (forall a. a) -> b(forall a. a) -> (forall b. b) 。)

Let me rewrite the example using a System-F-like notation, wherein we pass types as well. 让我使用类似System-F的表示法重写示例,其中我们也传递类型。 Below, \\\\ stands for type-abstraction (big lambda), as well as dictionary abstraction. 下面, \\\\代表类型抽象(大型lambda),以及字典抽象。 Also, @ stands for type/dictionary application. 此外, @代表类型/字典应用程序。

Before doing that, recall the type of (.) : 在此之前,请回忆一下(.)的类型:

(.) :: forall a b c . (b -> a) -> (c -> b) -> (c -> a)

Here's the annotated code (beware, not for the faint of heart): 这是带注释的代码(小心,不是为了胆小的人):

fComp :: F b c -> F a b -> F a c
fComp (f :: forall f1. Functor f1 => f1 b -> f1 c)
      (f':: forall f2. Functor f2 => f2 a -> f2 b) 
   = \\ ff :: (* -> *) ->
     \\ ffD :: Functor ff ->
     ((.) @ (ff c) @ (ff b) @ (ff a))   -- instantiated composition
     (f  @ ff @ ffD)                    -- first argument of (.)
     (f' @ ff @ ffD)                    -- second argument of (.)

(Above I pretended a , b , c are type constants to avoid further type-level lambdas.) (上面我假装abc是类型常量,以避免进一步的类型级lambda。)

The important parts: 重要的部分:

  • f and f' are being used at specific types. ff'用于特定类型。 That is they are being applied to type-level arguments before being fed to (.) . 也就是说,它们在被送到(.)之前被应用于类型级参数。
  • (.) is being applied at the type level to types ( ff c , etc) which are not the polytypes of f and f' (.)在类型级别应用于类型( ff c等),它们不是ff'

As you can see, the original code is far from being trivial. 正如您所看到的,原始代码远非微不足道。 Type inference is able to add the needed type-level lambdas and applications. 类型推断能够添加所需的类型级lambda和应用程序。 After those are added, we can not simply eta contract fComp anymore. 在添加这些之后,我们不能简单地再签订fComp合同。

In the pointfree variant, type inference would need to do more than in the pointful case. 在pointfree变体中,类型推断需要做的不仅仅是在有意义的情况下。 While the first argument of fComp is of type F ab , the first argument of (.) must be of the form x -> y , which does not unify for F ab = forall g . ... 虽然fComp的第一个参数是F ab类型,但是(.)的第一个参数必须是x -> y的形式,它不会统一为F ab = forall g . ... F ab = forall g . ... . F ab = forall g . ... Indeed, there's no way to successfully solve the typing attempt below: 实际上,没有办法成功解决下面的打字尝试:

fComp :: F b c -> F a b -> F a c
fComp 
   = \\ ff :: (* -> *) ->
     \\ ffD :: Functor ff ->
     ((.) @ ???a @ ???b @ ???c)

Above there's no ???a ,... that can lead to the wanted type. 上面没有???a ,...可以导致想要的类型。

The only possibility would be to instantiate the forall -quantified variables hidden in the F xy types, but to do that we need a point . 唯一的可能性是实例化隐藏在F xy类型中的forall -quantified变量,但要做到这一点 ,我们需要一个 The compiler might eta-expand that code for you so that points appear and so can be instantiated, theoretically speaking, but in practice will not. 编译器可能会为您扩展该代码,以便出现点,从理论上讲可以实例化,但实际上不会。

(Also, eta expansion is not always valid in Haskell: eg seq (undefined::()->()) 3 loops while seq (\\x->(undefined::()->()) x) 3 returns 3 ). (此外,eta扩展在Haskell中并不总是有效的:例如seq (undefined::()->()) 3循环而seq (\\x->(undefined::()->()) x) 3返回3 )。

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

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