Pourquoi le toSeq de Scala convertit-il un jeu immuable en un Mutable ArrayBuffer?
Si j'appelle toSeq
sur un immuable Set
collection je reçois un ArrayBuffer
.
scala> Set(1,2,3).toSeq // returns Seq[Int] = ArrayBuffer(1, 2, 3)
cela me surprend. Étant donné L'accent mis par Scala sur l'utilisation de structures de données immuables, Je m'attends à récupérer une séquence immuable comme un Vector
ou List
au lieu d'un mutable ArrayBuffer
. L'ordre retourné des éléments de l'ensemble devrait bien sûr être indéfini, mais il ne semble pas y avoir de raison sémantique pour laquelle cet ordre devrait également être mutable.
en général, je m'attends Scala fonctionne pour produire toujours des résultats immuables à moins que je demande explicitement un mutable. C'était mon hypothèse depuis le début, mais c'est une fausse ici, et je viens de passer une heure à déboguer un problème où la présence inattendue d'un ArrayBuffer
conduit à une erreur d'exécution dans un match
déclaration. Ma solution était de changer Set(...).toSeq
Set(...).toList
, mais cela ressemble à un piratage car il n'y a rien dans mon application qui nécessite une liste en particulier à ce point.
avoir Set(...).toSeq
rendre un objet mutable un défaut dans la mise en œuvre de Scala, ou y a-t-il un principe que je ne comprends pas ici?
ici Scala 2.9.2.
2 réponses
Ici est le fil récent sur la question de savoir si Seq devrait signifier immuable.Seq.
Roland Kuhn:
de la collection.Seq ne pas avoir de mutateurs n'est pas du tout une défense valable!
l'exemple de varargs mutables est plutôt sournois.
Récemment,
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)
je suis d'accord c'est un peu étrange, mais je ne crois pas qu'il s'agit d'une faille. Tout d'abord, considérez ceci: le type de compilation Set.toSeq
() => Seq[Int]
() => ArrayBuffer[Int]
ArrayBuffer
se trouve juste être le type d'exécution de l'objet retourné (probablement parce que Set.toSeq
ajoute un ArrayBuffer
et ensuite retourne juste cela sans conversion).
Donc, même si toSeq
vous rend un objet mutable, vous ne pouvez pas réellement le muter (sans moulage, ou pattern matching pour ArrayBuffer
-- donc la vraie partie "étrange" est que Scala vous permet de faire correspondre les motifs sur des classes arbitraires). (Vous devez faire confiance que Set
ne pas se tenir à l'objet et muter, mais je pense que c'est une hypothèse juste à faire).
une autre façon de le voir est qu'un type mutable est strictement plus spécifique qu'un type immuable. Ou, une autre façon de dire cela est, chaque mutable objet peut aussi être traité comme un objet immuable: un objet immuable a un getter, et un objet mutable a un getter et un setter -- mais il a toujours un getter.
bien sûr, cela peut être abusé:
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
mais, bon, beaucoup de choses aussi.