具有生存类型的Scala列表:`map {case t => ...}`有效,`map {t => ...}`不是吗? [英] Scala lists with existential types: `map{ case t => ... }` works, `map{ t => ... }` doesn't?

查看:93
本文介绍了具有生存类型的Scala列表:`map {case t => ...}`有效,`map {t => ...}`不是吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们定义了一个存在类型:

Suppose that we have defined an existential type:

type T = (X => X, X) forSome { type X }

,然后定义类型为List[T]的列表:

and then defined a list of type List[T]:

val list = List[T](
  ((x: Int) => x * x, 42),
  ((_: String).toUpperCase, "foo")
)

众所周知[ 1 ],[ 2 ],尝试进行map的以下尝试无效:

It is well known [1], [2] that the following attempt to map does not work:

list.map{ x => x._1(x._2) }

但是,为什么下面的方法起作用?

But then, why does the following work?:

list.map{ case x => x._1(x._2) }

请注意,两个链接问题的答案都假定在模式匹配中需要类型变量,但在没有类型变量的情况下也可以使用.问题的重点更多地放在 { case x => ... }为什么起作用?.

Note that answers to both linked questions assumed that a type variable is required in the pattern matching, but it also works without the type variable. The emphasis of the question is more on Why does the { case x => ... } work?.

推荐答案

(我自己试图回答这个问题;应该不太错,但可能有点肤浅.)

首先,请注意

list.map{ x => x._1(x._2) }
list.map{ case x => x._1(x._2) }

本质上与

list map f1
list map f2

val f1: T => Any = t => t._1(t._2)
val f2: T => Any = _ match {
  case q => q._1(q._2)
}

实际上,f1的编译失败,而f2的编译成功.

Indeed, compilation of f1 fails, whereas f2 succeeds.

我们可以看到为什么f1的编译必须失败:

We can see why the compilation of f1 has to fail:

  1. t的类型为(X => X, X) forSome { type X }
  2. 因此,推断第一个组件t._1具有类型(X => X) forSome { type X }.
  3. 同样,第二个组件t._2被推断为具有X forSome { type X }类型,即Any.
  4. 我们不能将(X => X) forSome { type X }应用于Any,因为对于某些SuperSpecialType,实际上它可能是(SuperSpecialType => SuperSpecialType).
  1. t is of type (X => X, X) forSome { type X }
  2. Therefore, the first component t._1 is inferred to have the type (X => X) forSome { type X }.
  3. Likewise, the second component t._2 is inferred to have the type X forSome { type X }, which is just Any.
  4. We cannot apply an (X => X) forSome { type X } to Any, because it actually could turn out to be (SuperSpecialType => SuperSpecialType) for some SuperSpecialType.

因此,f1的编译应该会失败,并且的确会失败.

Therefore, compilation of f1 should fail, and it indeed does fail.

要了解为什么f2成功编译,可以查看typechecker的输出.如果我们将其另存为someFile.scala:

To see why f2 compiles successfully, one can look at the output of the typechecker. If we save this as someFile.scala:

class O {
  type T = (X => X, X) forSome { type X }

  def f2: T => Any = t => t match {
    case q => q._1(q._2)
  }

  def f2_explicit_func_arg: T => Any = t => t match {
    case q => {
      val f = q._1
      val x = q._2
      f(x)
    }
  }
}

,然后使用

$ scalac -Xprint:typer someFile.scala 

我们获得了本质上(去除了一些噪音):

we obtain essentially (with some noise removed):

class O extends scala.AnyRef {
  type T = (X => X, X) forSome { type X };
  def f2: O.this.T => Any = ((t: O.this.T) => t match {
    case (q @ _) => q._1.apply(q._2)
  });
  def f2_explicit_func_arg: O.this.T => Any = ((t: O.this.T) => t match {
    case (q @ _) => {
      val f: X => X = q._1;
      val x: X = q._2;
      f.apply(x)
    }
  })
}

第二个f2_explicit_func_arg版本(等效于f2)比较短的原始f2版本更具启发性.在f2_explicit_func_arg的经过删除和类型检查的代码中,我们看到X类型奇迹般地出现了,而类型检查器确实推断出了:

The second f2_explicit_func_arg version (equivalent to f2) is more enlightening than the shorter original f2-version. In the desugared and type-checked code of f2_explicit_func_arg, we see that the type X miraculously reappears, and the typechecker indeed infers:

f: X => X
x: X

所以f(x)确实有效.

在使用显式命名的类型变量的更明显的解决方法中,在这种情况下,我们手动执行编译器为我们执行的操作.

In the more obvious work-around with an explicitly named type variable, we do manually what the compiler does for us in this case.

我们也可以写:

type TypeCons[X] = (X => X, X)
list.map{ case t: TypeCons[x] => t._1(t._2) }

或更明确地:

list.map{ case t: TypeCons[x] => {
  val func: x => x = t._1
  val arg: x = t._2
  func(arg)
}}

和这两个版本的编译原因与f2完全相同.

and both versions would compile for very much the same reasons as f2.

这篇关于具有生存类型的Scala列表:`map {case t => ...}`有效,`map {t => ...}`不是吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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