Почему 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.
Ответы
Ответ 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
но, ну, может быть, много чего.