3

我正在使用 F 有界类型以便能够返回当前类型

trait Board[T <: Board[T]] {
  def updated : T
}

我正在尝试编写一个使用它的通用辅助方法。

问题是:为什么下面的不编译?

object BoardOps {
  def updated(board: Board[_]) = {
    board.updated.updated
  }
}

错误是value updated is not a member of _$1

我已经想出了这两种解决方法。它们是等价的吗?

object BoardOps {
  def updated(board: Board[_<:Board[_]]) = {
    board.updated.updated
  }
}

object BoardOps {
  def updated[T <: Board[T]](board: T) : T = {
    board.updated.updated
  }
}
4

2 回答 2

5

为什么以下不编译?

用作参数类型告诉编译器Board[_]“我不关心板内参数的类型”。也就是说,对于编译器来说,这是一个存在类型,它不知道关于该类型的任何细节。因此,board.updated返回“不可描述”或不透明类型,因为我们告诉编译器“丢弃”该类型信息。

我已经想出了这两种解决方法。它们是等价的吗?

您的前一个示例使用带有约束的存在类型作为 的子类型Board[_],或者更正式地说,我们编写:

Board[T] forSome { type T <: Board[U] }

编译器实际命名的位置T -> $_1U -> $_2

同样,我们对内部类型参数一无所知,只知道它有一个上限Board[_]。您的后一个示例使用名为 的通用量化类型T,编译器可以使用该类型将方法的返回类型推断为特定类型T而不是Any.

要回答您的问题,不,它们不等效。存在的和普遍量化的类型是对 的。有关存在类型的更多信息,请参阅什么是存在类型?https://www.drmaciver.com/2008/03/existential-types-in-scala/

于 2018-03-31T14:34:55.703 回答
0

它不会编译,因为一旦您编写Board[_],编译器就不会推断出关于匿名类型参数的任何有用信息_

有几种解决方法(与您已经提出的不同):

  1. 利用Board[X] forSome { type X <: Board[X] }
  2. 使用模式匹配来推断有关类型的更多信息

使用forSome存在量化

这可以通过forSome-existential 量化轻松解决:

import scala.language.existentials

object BoardOps_forSome {
  def updated(board: Board[X] forSome { type X <: Board[X] }) = {
    board.updated.updated.updated
  }
}

这使您可以updated无限次调用。


使用模式匹配

您实际上可以在不更改签名的情况下使用模式匹配来解决它。例如,这种不敬虔的构造允许您应用该方法updated三次(工作次数不受限制):

object BoardOps_patternMatch {
  def updated(board: Board[_]) = {
    board match {
      case b: Board[x] => b.updated match {
        case c: Board[y] => c.updated match {
          case d: Board[z] => d.updated
        }
      }
    }
  }
}

这是因为一旦将未知类型绑定到类型变量x, y, z,编译器就会被迫做一些额外的推理工作,并推断它实际上必须是x <: Board[_$?]等等。不幸的是,它一次只进行一个步骤,因为如果它试图计算最精确的类型,类型计算就会发散。


上限和全称不一样

请注意,您的第一个解决方法仅适用于两次:

object BoardOps_upperBound_once {
  def updated(board: Board[_<:Board[_]]) = {
    board.updated.updated // third .updated would not work
  }
}

因此,它不等同于您的第二个解决方法,它也可以无限次工作,这里以三个调用为例updated

object BoardOps_genericT {
  def updated[T <: Board[T]](board: T) : T = {
    board.updated.updated.updated
  }
}
于 2018-03-31T15:30:13.860 回答