为什么Set是一个函数?

kir*_*uku 15 math scala

在Scala中,a Set是一个函数:

trait Set[A] extends (A => Boolean)
Run Code Online (Sandbox Code Playgroud)

这使得不可能具有协变不可变Set因为类型A发生在逆变位置.相反Seq,未定义为函数.关于为什么以这种方式设计集合和序列的问题已经有一些内容:

一个答案说,其原因是数学背景.但这个答案没有多解释.那么,定义一个Set函数的具体优势是什么?如果以不同的方式实现它会有什么缺点?

axe*_*l22 11

类型集Set[A]必须有一个方法来测试类型的元素A是否在集合中.此方法(apply)必须具有A表示该元素的类型参数,并且该参数处于逆变位置.这意味着集合在其类型参数中不能协变A.所以 - 不是函数接口的扩展使得不可能有协变的不可变集,它是逆变apply方法的存在.

并且出于方便的原因,扩展Function1接口以便能够传递集合并将它们视为函数是有意义的.

相比之下,序列抽象没有一个方法来测试元素是否在序列中,它只有索引方法 - apply获取整数索引,并返回该索引处的元素.序列也被定义为函数,但类型的函数Int => A(它们是协变的A),而不是A => Boolean作为集合.

如果您想要了解更多关于如何在类型参数中将集合定义为协变的类型安全性A,请参阅此示例,其中set实现为私有成员写入一些内容,以便缓存查找(下面@uV是注释,禁用方差检查,expensiveLookup如果元素在集合中,则模拟对计算成本高的检查的调用:

import annotation.unchecked.{uncheckedVariance => uV}

trait Set[+A] {
  def apply(elem: A @uV): Boolean
}

class CachingSet[+A >: Null] extends Set[A] {
  private var lastLookup: (A @uV, Boolean) = (null, false)
  private def expensiveLookup(elem: A @uV) = (elem, true)
  def apply(elem: A @uV): Boolean = {
    if (elem != lastLookup._1) lastLookup = expensiveLookup(elem)
    lastLookup._2
  }
  def lastQueriedElement: A = lastLookup._1
}

object Main extends App {
  val css = new CachingSet[String]
  val csa: CachingSet[AnyRef] = css

  csa.apply(new AnyRef)
  val s: String = css.lastQueriedElement // you'll get a ClassCastException here
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*ral 6

相反,Seq未定义为函数.

不对.

Seq[T] extends (Int) => T
Run Code Online (Sandbox Code Playgroud)