Подтвердить что ты не робот

Почему Scala toSeq конвертирует неизменяемый Set в изменяемый массив ArrayBuffer?

Если я вызываю toSeq в неизменяемой коллекции Set, я получаю ArrayBuffer.

scala> Set(1,2,3).toSeq // returns Seq[Int] = ArrayBuffer(1, 2, 3)

Это меня удивляет. Учитывая Scala акцент на использовании неизменяемых структур данных, я ожидаю вернуть неизменяемую последовательность, такую ​​как Vector или List вместо изменчивого ArrayBuffer. Возвращаемое упорядочение элементов набора должно быть, конечно, undefined, но, похоже, не существует какой-либо семантической причины, почему это упорядочение также должно быть изменчивым.

В общем, я ожидаю, что операции Scala всегда будут давать неизменные результаты, если я явно не запрошу mutable. Это было мое предположение, но оно неверно здесь, и я просто потратил час на отладку проблемы, когда неожиданное присутствие ArrayBuffer привело к ошибке выполнения в инструкции match. Мое исправление заключалось в том, чтобы изменить Set(...).toSeq на Set(...).toList, но это похоже на взломать, потому что ничего не нужно в моем приложении, которое требует, в частности, списка в этой точке.

Имеет ли Set(...).toSeq возвращаемый изменчивый объект дефект в реализации Scala или существует принцип, который я неправильно понимаю здесь?

Это Scala 2.9.2.

4b9b3361

Ответ 1

Здесь - это недавний поток о том, должен ли Seq означать непреложный.

Роланд Кун:

collection.Seq, не имеющий мутаторов, вовсе не действительная защита!

Пример изменчивых varargs довольно подлый.

Недавно

scala> Set(1,2,3)
res0: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> res0.toSeq
res1: Seq[Int] = ArrayBuffer(1, 2, 3)

scala> res0.to[collection.immutable.Seq]
res2: scala.collection.immutable.Seq[Int] = Vector(1, 2, 3)

Ответ 2

Я согласен, что это немного странно, но я не считаю, что это недостаток. Во-первых, рассмотрим это: тип времени компиляции Set.toSeq равен

() => Seq[Int]

не

() => ArrayBuffer[Int]

ArrayBuffer просто является типом времени выполнения возвращаемого объекта (возможно, потому, что Set.toSeq добавляет к ArrayBuffer, а затем просто возвращает это без преобразования).

Итак, хотя toSeq возвращает вам измененный объект, вы не можете его мутировать (без каста или соответствия шаблону ArrayBuffer), поэтому реальной "странной" частью является то, что Scala позволяет сопоставлять шаблоны произвольным классам). (Вы должны верить, что Set не держится за объект и не изменяет его, но я думаю, что это справедливое предположение).

Еще один способ взглянуть на это: изменяемый тип просто строго определен, чем неизменный тип. Или, еще один способ сказать это, каждый измененный объект можно также рассматривать как непреложный объект: неизменяемый объект имеет геттер, а изменчивый объект имеет геттер и сеттер, но у него все еще есть геттер.

Конечно, это можно злоупотреблять:

val x = new Seq[Int] {
    var n: Int = 0
    def apply(k: Int) = k
    def iterator = {
        n += 1
        (0 to n).iterator
    }
    def length = n
}

x foreach println _
0
1

x foreach println _
0
1
2

но, ну, может быть, много чего.