存在类型表达式的摩擦化

时间:2016-08-23 21:10:28

标签: scala existential-type

在Scala中,以下表达式引发了类型错误:

val pair: (A => String, A) forSome { type A } = ( { a: Int => a.toString }, 19 )
pair._1(pair._2)

正如SI-9899和此answer中所述,根据规范,这是正确的:

  

我认为这是按照SLS 6.1设计的:“以下内容   skolemization规则普遍适用于每个表达式:如果   表达式的类型将是存在类型T,然后是类型   该表达式被假定为T的一个重要化。“

但是,我并不完全理解这一点。此规则适用于哪一点?它是否适用于第一行(即,pair的类型与类型注释给出的类型不同),或者在第二行中应用(但将规则应用于第二行作为整体不会导致到类型错误)?

我们假设SLS 6.1适用于第一行。它应该是skolemize存在类型。我们可以通过将存在性放在类型参数中来使第一行中的非存在类型:

case class Wrap[T](x:T)
val wrap = Wrap(( { a: Int => a.toString }, 19 ) : (A => String, A) forSome { type A })
wrap.x._1(wrap.x._2)

有效! (没有类型错误。)这意味着,当我们定义pair时,存在类型会“丢失”吗?号:

val wrap2 = Wrap(pair)
wrap2.x._1(wrap2.x._2)

此类型检查!如果它是pair赋值的“错误”,那么这应该不起作用。因此,它不起作用的原因在于第二行。如果是这种情况,wrappair示例之间有什么区别?

总结一下,这里还有一对例子:

val Wrap((a2,b2)) = wrap
a2(b2)

val (a3,b3) = pair
a3(b3)

两者都不起作用,但是通过类比wrap.x._1(wrap.x._2)进行类型检查的事实,我会认为a2(b2)也可以进行类型检查。

1 个答案:

答案 0 :(得分:4)

我相信我找出了上面表达式的大部分过程。

首先,这是什么意思:

  

以下的石油化规则适用于所有人   表达式:如果表达式的类型是存在类型   T,然后假设表达式的类型为a   t [SLS 6.1]

的skolemization

这意味着只要确定表达式或子表达式具有类型T[A] forSome {type A},就会选择新的类型名称A1,并且表达式的类型为T[A1]。这是有道理的,因为T[A] forSome {type A}直观地表示存在某种类型A,使得表达式具有类型T[A]。 (选择什么名称取决于编译器实现。我使用A1将其与绑定类型变量A区分开来)

我们看第一行代码:

val pair: (A => String, A) forSome { type A } = ({ a: Int => a.toString }, 19)

这里实际上还没有使用skolemization规则。 ({ a: Int => a.toString }, 19)的类型为(Int=>String, Int)。这是(A => String, A) forSome { type A }的子类型,因为存在A(即Int),因此rhs的类型为(A=>String,A)

pair现在有(A => String, A) forSome { type A }类型。

下一行是

pair._1(pair._2)

现在,typer从内到外为子表达式分配类型。首先,pair的第一次出现是一个类型。回想一下,pair的类型为(A => String, A) forSome { type A }。由于skolemization规则适用于每个子表达式,我们将其应用于第一个pair。我们选择一个新的类型名称A1,并将pair键入(A1 => String, A1)。然后我们为第二次出现的pair分配一个类型。同样适用于skolemization规则,我们选择另一个新鲜类型名称A2,第二次出现pair类型为(A2=>String,A2)

然后pair._1的类型为A1=>Stringpair._2的类型为A2,因此pair._1(pair._2)的输入效果不佳。

请注意,这不是特殊规则" s"错误"键入失败。如果我们没有skolemization规则,pair._1将输入(A=>String) forSome {type A}pair._2将输入A forSome {type A},这与Any相同。然后pair._1(pair._2)仍然不会打字。 (skolemization规则实际上有助于制作类型,我们将在下面看到。)

那么,为什么Scala拒绝理解pair的两个实例实际上属于{em>同一(A=>String,A)A类型?在val pair的情况下,我不知道一个很好的理由,但是例如,如果我们有一个相同类型的var pair,编译器就不能使用相同的{{1}来删除它的几次出现。 }}。为什么?想象一下,在表达式中,A1的内容会发生变化。首先它包含pair,然后在表达式的评估结束时,它包含(Int=>String, Int)。如果(Bool=>String,Bool)的类型为pair,则表示可以。但是,如果计算机同时出现两个(A=>String,A) forSome {type A}相同的skolemized类型pair,则输入将不正确。同样,如果(A1=>String,A1)pair,它可能会在不同的调用中返回不同的结果,因此不能使用相同的def pair进行特定的处理。对于A1,此参数不成立(因为val pair无法更改),但我认为类型系统会变得过于复杂,如果它会尝试处理与val pair不同的val pair {1}}。 (另外,有些情况下var pair可以改变内容,即从单元化到初始化。但我不知道在这种情况下是否会导致问题。)

但是,我们可以使用skolemization规则使val输入良好。第一次尝试是:

pair._1(pair._2)

为什么要这样做? val pair2 = pair pair2._1(pair2._2) 类型为pair。因此,对于一些新鲜的(A=>String,A) forSome {type A},它的类型变为(A3=>String,A3)。因此,新的A3应该被赋予类型val pair2(rhs的类型)。如果(A3=>String,A3)的类型为pair2,那么(A3=>String,A3)的输入效果会很好。 (不再存在任何存在感。)

不幸的是,由于规范中的另一条规则,这实际上不起作用:

  

如果值定义不是递归的,则可以省略类型T,   在这种情况下,假定表达式e的打包类型。 [SLS 4.1]

打包类型与skolemization相反。这意味着,由于skolemization规则而在表达式中引入的所有新变量现在都转换回存在类型。也就是说,pair2._1(pair2._2)变为T[A1]

因此,在

T[A] forSome {type A}

val pair2 = pair 实际上将被赋予类型pair2,即使rhs被赋予类型(A=>String,A) forSome {type A}。然后(A3=>String,A3)将不会输入,如上所述。

但我们可以使用另一种技巧来达到预期的效果:

pair2._1(pair2._2)

乍一看,这是一个毫无意义的模式匹配,因为pair match { case pair2 => pair2._1(pair2._2) } 刚刚被分配pair2,所以为什么不使用pair?原因是SLS 4.1中的规则仅适用于pairval。变量模式(如此处var)不受影响。因此,pair2的类型为pair(A4=>String,A4)的类型相同(不是打包类型)。然后pair2输入pair2._1A4=>String输入pair2._2,所有输入都很好。

因此,A4形式的代码片段可用于"升级" x match { case x2 =>新的"伪值" x可以使用x2创建一些输入不好的表达式。 (我不知道为什么规范不会简单地允许在我们写x时发生同样的事情。由于我们没有得到额外的缩进级别,因此读取肯定会更好。)

在这次短途旅行之后,让我们来看看问题中其余表达式的输入:

val x2 = x

此处表达式val wrap = Wrap(({ a: Int => a.toString }, 19) : (A => String, A) forSome { type A }) 类型为({ a: Int => a.toString }, 19)。类型大小写使其成为类型的表达式 (Int=>String,Int)。然后应用了skolemization规则,因此表达式((A => String, A) forSome { type A })的参数,即)获得了新Wrap的类型(A5=>String,A5)。我们对它应用A5,并且rhs的类型为Wrap。要获取Wrap[(A5=>String,A5)]的类型,我们需要再次应用SLS 4.1中的规则:我们计算wrap的打包类型Wrap[(A5=>String,A5)]。因此,Wrap[(A=>String,A)] forSome {type A}的类型为wrap(而非Wrap[(A=>String,A)] forSome {type A},乍看之下可能会出现!)请注意,我们可以通过运行编译器确认Wrap[(A=>String,A) forSome {type A}]具有此类型选项wrap

我们现在输入

-Xprint:typer

此处,skolemization规则适用于wrap.x._1(wrap.x._2) 的两次出现,并分别输入wrapWrap[(A6=>String,A6)]。然后Wrap[(A7=>String,A7)]的类型为wrap.x._1A6=>String的类型为wrap.x._2。因此A7的格式不合适。

但是编译器不同意并接受wrap.x._1(wrap.x._2)!我不知道为什么。 Scala类型系统中有一些我不了解的额外规则,或者它只是一个编译器错误。使用wrap.x._1(wrap.x._2)运行编译器也不会提供额外的见解,因为它没有注释-Xprint:typer中的子表达式。

接下来是:

wrap.x._1(wrap.x._2)

此处val wrap2 = Wrap(pair) 的类型为pair,并且要(A=>String,A) forSome {type A}。然后(A8=>String,A8)类型为Wrap(pair)Wrap[(A8=>String,A8)]获取打包类型wrap2。即,Wrap[(A=>String,A)] forSome {type A}wrap2具有相同的类型。

wrap

wrap2.x._1(wrap2.x._2) 一样,这不应该输入,但确实如此。

wrap.x._1(wrap.x._2)

我们在这里看到一条新规则:[SLS 4.1](不是上面引用的部分)解释了这种模式匹配val Wrap((a2,b2)) = wrap 语句扩展为:

val

现在我们可以看到val tmp = wrap match { case Wrap((a2,b2)) => (a2,b2) } val a2 = tmp._1 val b2 = tmp._2 获取新(a2,b2)的类型(A9=>String,A9), 由于打包类型规则,A9获取类型tmp。然后(A=>String,A) forSome A使用skolemization规则获取类型tmp._1A10=>String通过压缩类型规则获取类型val a2(A=>String) forSome {type A}使用skolemization规则获取类型tmp._2A11通过压缩类型规则获取类型val b2(这与A forSome {type A}相同)。< / p>

因此

Any

格式不正确,因为a2(b2) 获取了类型a2A12=>String从类别化规则中获取了类型b2

类似地,

A13=>String

扩展为

val (a3,b3) = pair

然后val tmp2 = pair match { case (a3,b3) => (a3,b3) } val a3 = tmp2._1 val b3 = tmp2._2 按打包类型规则获取tmp2类型,(A=>String,A) forSome {type A}val a3获取类型val b3(A=>String) forSome {type A}(又名{ {1}}),分别为。

然后

A forSome {type A}
由于Any没有与<{1}}相同的原因,

输入不是很好。