scala - 在对象中没有定义的类型声明是什么意思?

标签 scala types implicit abstract-type

Scala 允许使用 type 定义类型关键字,根据它们的声明时间,它们的含义和目的通常略有不同。

如果您使用 type在对象或包对象内,您需要定义一个类型别名,即另一种类型的更短/更清晰的名称:

package object whatever {
  type IntPredicate = Int => Boolean

  def checkZero(p: IntPredicate): Boolean = p(0)
}

在类/特征中声明的类型通常旨在在子类/子特征中被覆盖,并且最终也被解析为具体类型:
trait FixtureSpec {
  type FixtureType
  def initFixture(f: FixtureType) = ...
}

trait SomeSpec extends FixtureSpec {
  override type FixtureType = String

  def test(): Unit = {
    initFixture("hello")
    ...
  }
}

抽象类型声明还有其他用途,但无论如何它们最终都会解析为一些具体类型。

但是,还有一个选项可以在对象内声明抽象类型(即没有实际定义):
object Example {
  type X
}

这会编译,而不是例如抽象方法:
object Example {
  def method: String  // compilation error
}

因为对象不能被扩展,所以它们永远不能解析为具体类型。

我认为这样的类型定义可以方便地用作幻像类型。例如(使用 Shapeless' 标记类型):
import shapeless.tag.@@
import shapeless.tag

type ++>[-F, +T]

trait Converter

val intStringConverter: Converter @@ (String ++> Int) = tag[String ++> Int](...)

但是,似乎类型系统对待这些类型的方式与常规类型不同,这导致上述“抽象”类型的使用在某些情况下会失败。

特别是,在查找隐式参数时,Scala 最终会查看与“关联”类型相关联的隐式范围,即存在于隐式参数的类型签名中的类型。然而,当使用“抽象”类型时,这些关联类型的嵌套似乎存在一些限制。考虑这个示例设置:
import shapeless.tag.@@

trait Converter

type ++>[-F, +T]

case class DomainType()

object DomainType {
  implicit val converter0: Converter @@ DomainType = null
  implicit val converter1: Converter @@ Seq[DomainType] = null
  implicit val converter2: Converter @@ (Seq[String] ++> Seq[DomainType]) = null

}

// compiles
implicitly[Converter @@ DomainType]
// compiles
implicitly[Converter @@ Seq[DomainType]]
// fails!
implicitly[Converter @@ (Seq[String] ++> Seq[DomainType])]

在这里,前两个隐式解析编译得很好,而最后一个失败了,错误是缺少隐式。如果我在与 implicitly 相同的范围内定义隐式调用,然后编译:
implicit val converter2: Converter @@ (Seq[String] ++> Seq[DomainType]) = null
// compiles
implicitly[Converter @@ (Seq[String] ++> Seq[DomainType])]

但是,如果我更改 ++>定义为 trait而不是 type :
trait ++>[-F, +T]

那么所有 implicitly上面的调用编译就好了。

因此,我的问题是,这种类型声明的目的究竟是什么?它们旨在解决什么问题,为什么它们不被禁止,就像对象中的其他类型的抽象成员一样?

最佳答案

对于方法(或值),只有 2 个选项:要么它有主体(然后它是“具体的”),要么没有(那么它是“抽象的”)。 A型X总是某种类型的区间 X >: LowerBound <: UpperBound (如果 LowerBound = UpperBound,我们称之为具体,如果 LowerBound = NothingUpperBound = Any,我们称之为完全抽象,但它们之间有多种情况)。所以如果我们想禁止对象中的抽象类型,我们应该总是有办法检查类型 LowerBoundUpperBound是平等的。但是它们可以用一些复杂的方式定义,通常这样的检查并不那么容易:

object Example {
  type X >: N#Add[N] <: N#Mult[Two] // Do we expect that compiler proves n+n=n*2?
}

关于scala - 在对象中没有定义的类型声明是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54137522/

相关文章:

windows - Spark 发射 : find version

scala - scala 编译器对类型系统内的单元类型有什么特殊的规则

powershell - 为什么在 PowerShell 中将 PSCustomObjects 转换为带有 DateTime 的自定义类型失败?

scala - 在 Scala 中提供类型类的实例时使用 vals 还是 object 更好

scala - Scala隐式隐式

java - 在 Spark 1.6.0 上,获取与 spark.driver.maxResultSize 相关的 org.apache.spark.SparkException

arrays - 尝试将 json 数组解析为 Scala 中的 List[Object] 时出现验证错误

postgresql - 用于循环平均的自定义 PostgreSQL 聚合

c++ - decltype(std::get<0>(tup)) 到底是什么?

scala - 为什么对于任何给定的类型参数,类型类只能有一种实现?