在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
赋值的“错误”,那么这应该不起作用。因此,它不起作用的原因在于第二行。如果是这种情况,wrap
和pair
示例之间有什么区别?
总结一下,这里还有一对例子:
val Wrap((a2,b2)) = wrap
a2(b2)
val (a3,b3) = pair
a3(b3)
两者都不起作用,但是通过类比wrap.x._1(wrap.x._2)
进行类型检查的事实,我会认为a2(b2)
也可以进行类型检查。
答案 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=>String
,pair._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中的规则仅适用于pair
和val
。变量模式(如此处var
)不受影响。因此,pair2
的类型为pair
,(A4=>String,A4)
的类型相同(不是打包类型)。然后pair2
输入pair2._1
,A4=>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)
的两次出现,并分别输入wrap
和Wrap[(A6=>String,A6)]
。然后Wrap[(A7=>String,A7)]
的类型为wrap.x._1
,A6=>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._1
,A10=>String
通过压缩类型规则获取类型val a2
。 (A=>String) forSome {type A}
使用skolemization规则获取类型tmp._2
,A11
通过压缩类型规则获取类型val b2
(这与A forSome {type A}
相同)。< / p>
因此
Any
格式不正确,因为a2(b2)
获取了类型a2
而A12=>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}}相同的原因,输入不是很好。