В коллекциях Scala 2.8, почему был добавлен тип Traversable выше Iterable?

Я знаю, что для Traversable вам нужен только метод foreach. Iterable требуется метод iterator.

Оба файла SID Scala 2.8 SID и бумага "Fighting Bitrot with Types" в основном молчат о том, почему добавлен Traversable. SID только говорит: "Дэвид Макивер... предложил Traversable как обобщение Iterable".

Я смутно собрался из дискуссий по IRC, что он связан с возвратом ресурсов, когда обход коллекции завершается?

Возможно, это связано с моим вопросом. В TraversableLike.scala есть некоторые нечетные определения функций, например:

def isEmpty: Boolean = {
  var result = true
  breakable {
    for (x <- this) {
      result = false
      break
    }
  }
  result
}

Я предполагаю, что есть веская причина, которая была написана не просто:

def isEmpty: Boolean = {
  for (x <- this)
    return false
  true
}

Ответ 1

Я спросил Дэвида Макивера об этом в IRC. Он сказал, что больше не помнит все причины, но они включали:

  • "итераторы часто раздражают... для реализации"

  • итераторы "иногда небезопасны (из-за установки/разрыва в начале и в конце цикла)"

  • Ожидаемая эффективность достигается за счет реализации некоторых вещей через foreach, а не с помощью итераторов (выигрыши необязательно пока еще не продемонстрированы с помощью текущего компилятора HotSpot)

Ответ 2

Я подозреваю, что одна из причин заключается в том, что гораздо проще написать конкретную реализацию для коллекции с абстрактным методом foreach, чем для одного с абстрактным методом iterator. Например, в С# вы можете написать реализацию метода GetEnumerator IEnumerable<T>, как если бы это был метод foreach:

IEnumerator<T> GetEnumerator() 
{
    yield return t1;
    yield return t2;
    yield return t3;
}

(Компилятор генерирует соответствующий конечный автомат для управления итерацией через IEnumerator.) В Scala вам нужно написать собственную реализацию Iterator[T] для этого. Для Traversable вы можете выполнить эквивалент вышеупомянутой реализации:

def foreach[U](f: A => U): Unit = {
  f(t1); f(t2); f(t3)
}

Ответ 3

как раз относительно вашего последнего вопроса:

def isEmpty: Boolean = {
  for (x <- this)
    return false
  true
}

Это грубо переведено компилятором на:

def isEmpty: Boolean = {
  this.foreach(x => return false)
  true
}

Таким образом, вы просто не можете вырваться из foreach, isEmpty всегда будет возвращать true.

Вот почему был создан "хакерский" Breakable, который вырывается из foreach, бросая Control-Exception, ловя его в breakable и возвращает.