使用成员访问权限而不是提取程序时的奇怪类型不匹配 [英] Strange type mismatch when using member access instead of extractor

查看:93
本文介绍了使用成员访问权限而不是提取程序时的奇怪类型不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出一个元素为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.valuewrite.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可以告诉 valuewriter具有相同的类型参数.

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):

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

  1. 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.valuewrite.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆