使用成员访问而不是提取器时出现奇怪的类型不匹配



给定一个元组,其中包含 A 类型的元素和另一种在 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 }

为什么使用元组的提取器,以下内容按预期工作:

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(以正确编译吗?

编辑

从欧文的想法出发,我尝试了是否可以让它在没有模式匹配的情况下工作(这是我首先想要的(。这里有两个更奇怪的案例,一个有效,另一个无效:

// 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))
}

对我来说仍然很晦涩...

-Xprint:typer一起跑步在这里很有启发性。test2的问题是有一种存在主义类型,它出现在两个不同的地方:两者都 write.valuewrite.writer都有存在主义的类型,但至关重要的是,编译器无法知道它们在存在上具有相同的量化类型变量。即使您从同一对象访问它们,编译器忘记了它们来自同一个地方。

完全键入test1后,您会看到:

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可以告诉该valuewriter具有相同的类型参数。

test2的解决方案是不使用存在主义:

def test2[A]( set: Set[ Write[ A ]], cache: Cache ) {
   set.foreach { write =>
      cache.store( write.value, write.writer )
   }
}

或者将类型变量与匹配项绑定:

def test2( set: Set[ Write[ _ ]], cache: Cache ) {
   set.foreach { case write: Write[a] =>
      cache.store( write.value, write.writer )
   }
}

编辑

让我努力回答你提出的新问题。

test3不起作用的原因是,当您编写:

set.foreach( process (

process 是多态的,必须成为单态的,原因有两个(我知道(:

  1. Scala 中的函数不能是多态的;只有方法可以是。 process定义为一种方法;当用作一等函数时,它是一个函数。

  2. 编译器进行类型推断的方式主要是采用多态值并将它们统一在一起以使它们不那么多态。将实际的多态值作为方法参数传递将需要更高等级的类型。

test4起作用的原因是函数文字:

set.foreach( w => process( w ))

实际上不是一个多态函数!它以一个非常合格的类型作为其论据;但不是多态类型。然后它调用方法(而不是函数(process,并将存在类型变量与process的类型参数匹配。很狂野,嗯?

你也可以写:

set.foreach( process(_) )

创建匿名函数意味着同样的事情。

您可能认为合适也可能不合适的另一条路线是丢弃存在类型和使用类型成员:

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.valuewrite.writer具有相同的类型参数,因为它们具有相同的路径相关类型。

最新更新