En las colecciones de Scala 2.8, ¿por qué se agregó el tipo Traversable arriba Iterable?
scala-2.8 scala-collections (3)
Le pregunté a David McIver sobre esto en IRC. Dijo que ya no recordaba todas las razones, pero que incluían:
"Los iteradores suelen ser molestos ... para implementar"
los iteradores son "a veces inseguros (debido a la configuración / desmontaje al principio y al final del ciclo)"
Las esperadas ganancias de eficiencia de la implementación de algunas cosas a través de foreach en lugar de a través de iteradores (ganancias no necesariamente demostradas en realidad con el compilador actual de HotSpot)
Sé que para ser Traversable
, solo necesitas tener un método foreach
. Iterable
requiere un método iterator
.
Tanto el SID de las colecciones Scala 2.8 como el documento "Combatiendo Bitrot con Tipos" son básicamente silenciosos sobre el tema de por qué se agregó Traversable
. El SID solo dice "David McIver ... propuso Traversable como una generalización de Iterable".
¿Me he reunido vagamente de las discusiones sobre IRC que tiene que ver con recuperar recursos cuando finaliza el recorrido de una colección?
Lo siguiente está probablemente relacionado con mi pregunta. Hay algunas definiciones de funciones de aspecto extraño en TraversableLike.scala
, por ejemplo:
def isEmpty: Boolean = {
var result = true
breakable {
for (x <- this) {
result = false
break
}
}
result
}
Supongo que hay una buena razón por la que no se escribió simplemente como:
def isEmpty: Boolean = {
for (x <- this)
return false
true
}
Sospecho que una razón es que es mucho más fácil escribir una implementación concreta para una colección con un método abstracto de foreach
que para una con un método de iterator
abstracto. Por ejemplo, en C # puede escribir la implementación del método GetEnumerator
de IEnumerable<T>
como si fuera un método foreach
:
IEnumerator<T> GetEnumerator()
{
yield return t1;
yield return t2;
yield return t3;
}
(El compilador genera una máquina de estado apropiada para conducir la iteración a través del IEnumerator
.) En Scala, tendría que escribir su propia implementación de Iterator[T]
para hacer esto. Para Traversable
, puede hacer el equivalente de la implementación anterior:
def foreach[U](f: A => U): Unit = {
f(t1); f(t2); f(t3)
}
sólo con respecto a su última pregunta:
def isEmpty: Boolean = {
for (x <- this)
return false
true
}
Esto se traduce aproximadamente por el compilador a:
def isEmpty: Boolean = {
this.foreach(x => return false)
true
}
Entonces, simplemente no puede salir de foreach, isEmpty siempre devolverá true.
Esta es la razón por la que "hacky" Breakable se construyó y se rompe en foreach lanzando una Control-Exception, atrapándola en breakable
y devuelve.