使用成员访问权限而不是提取程序时的奇怪类型不匹配 [英] Strange type mismatch when using member access instead of extractor
问题描述
给出一个元素为A
的元组,并在A
中参数化另一种类型:
Given a tuple with elements of type A
and another type parametrised in A
:
trait Writer[-A] { def write(a: A): Unit }
case class Write[A](value: A, writer: Writer[A])
和一个使用站点:
trait Cache { def store[A](value: A, writer: Writer[A]): Unit }
使用元组的提取器,为什么以下各项会按预期工作:
Why does the following work as expected, using the tuple's extractor:
def test1(set: Set[Write[_]], cache: Cache): Unit =
set.foreach {
case Write(value, writer) => cache.store(value, writer)
}
但是以下操作失败:
def test2(set: Set[Write[_]], cache: Cache ): Unit =
set.foreach { write =>
cache.store(write.value, write.writer)
}
有错误消息
found : Writer[_$1] where type _$1
required: Writer[Any]
cache.store(write.value, write.writer)
^
我可以修复第二种形式(test2
)以正确编译吗?
Can I fix the second form (test2
) to compile properly?
编辑
与Owen的想法不同,我尝试了在没有模式匹配的情况下也可以使它工作(这是我最初想要的).这是另外两种奇怪的情况,一种有效,另一种无效:
Departing from the ideas by Owen I tried out if I can make it work without pattern matching at all (which is what I wanted in the first place). Here are two more strange cases, one working, the other not:
// does not work
def test3(set: Set[Write[_]], cache: Cache): Unit = {
def process[A](write: Write[A]): Unit =
cache.store(write.value, write.writer)
set.foreach(process)
}
// _does work_
def test4(set: Set[Write[_]], cache: Cache): Unit = {
def process[A](write: Write[A]): Unit =
cache.store(write.value, write.writer)
set.foreach(w => process(w))
}
对我还是很晦涩...
Still pretty obscure to me...
推荐答案
在此处使用-Xprint:typer
运行. test2
的问题是
存在一个存在类型,它出现在两个不同的位置:两者
write.value
和write.writer
具有存在类型,但是至关重要的是,
编译器无法知道它们是否存在相同
量化类型变量.即使您从同一对象访问它们,
编译器会忘记它们来自同一位置.
Running with -Xprint:typer
is illuminating here. The problem with test2
is
that there is an existential type, which appears in two separate places: both
write.value
and write.writer
have an existential type, but, crucially, the
compiler has no way of knowing that they have the same existentially
quantified type variable. Even though you access them from the same object, the
compiler forgets they came from the same place.
完全键入test1
后,您将看到:
When test1
is fully typed, you see:
def test1(set: Set[Write[_]], cache: Cache) =
set.foreach(((x0$1: Write[_]) => x0$1 match {
case (value: _$1, writer: Writer[_$1])Write[_$1]((value @ _), (writer @ _)) =>
cache.store[_$1](value, writer)
}));
类型变量_$1
与值匹配.匹配类型变量_$1
会将其绑定到
case
的范围,所以它不再存在了,Scala可以告诉
value
和writer
具有相同的类型参数.
The type variable _$1
is matched along with the values. Matching the type variable _$1
binds it in
the scope of the case
, so it's not existential anymore, and Scala can tell
that value
and writer
have the same type parameter.
test2
的解决方案是不使用存在性:
The solution for test2
is to not use existentials:
def test2[A]( set: Set[ Write[ A ]], cache: Cache ) {
set.foreach { write =>
cache.store( write.value, write.writer )
}
}
或将类型变量与匹配项绑定:
or to bind the type variable with a match:
def test2( set: Set[ Write[ _ ]], cache: Cache ) {
set.foreach { case write: Write[a] =>
cache.store( write.value, write.writer )
}
}
修改
让我努力回答您提出的新问题.
Let me endeavor to answer the new questions you brought up.
test3
不起作用的原因是,在您写时:
The reason test3
does not work, is that when you write:
set.foreach(process)
set.foreach( process )
process
是多态的,由于两个原因(我知道),必须使其变为单态:
process
, which is polymorphic, has to be made monomorphic, for two reasons (that I know of):
-
Scala中的函数不能是多态的;只有方法可以.
process
定义为方法;当用作一等函数时,它是一个函数.
Functions in Scala cannot be polymorphic; only methods can be.
process
as defined as a method; when used as a first-class function, it is a function.
编译器进行类型推断的方式主要是通过获取多态值并将其统一在一起以减少其多态性.将实际的多态值作为方法参数传递将需要更高级别的类型.
The way the compiler does type inference is mostly by taking polymorphic values and unifying them together to make them less polymorphic. Passing an actual polymorphic value as a method argument would require higher-rank types.
test4
起作用起作用的原因是函数文字:
The reason that test4
does work is that the function literal:
set.foreach( w => process( w ))
实际上不是多态函数!它以一个能满足条件的类型作为参数.但不是多态类型.然后,它调用方法(不是函数)process
,并将存在的类型变量与process
的类型参数进行匹配.蛮狂野的,是吗?
is actually not a polymorphic function! It takes as its argument an exestentially qualified type; but not a polymorphic type. It then calls the method (not the function) process
, and matches the existential type variable to process
's type parameter. Pretty wild, eh?
您也可以写:
set.foreach( process(_) )
创建匿名函数的含义相同.
which, creating an anonymous function, means the same thing.
您可能会或可能不会找到的另一条路线是丢弃 存在类型和使用类型成员:
Another route you may or may not find appropriate would be to discard existential types and use type members:
trait Writable {
type A
val value: A
val writer: Writer[A]
}
case class Write[T]( value: T, writer: Writer[ T ]) extends Writable {
type A = T
}
def test2( set: Set[Writable], cache: Cache ) {
set.foreach { write =>
cache.store( write.value, write.writer )
}
}
在这里Scala可以看到write.value
和write.writer
具有相同的
类型参数,因为它们具有相同的路径依赖类型.
Here Scala is able to see that write.value
and write.writer
have the same
type parameter because they have the same path dependent type.
这篇关于使用成员访问权限而不是提取程序时的奇怪类型不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!