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(...).toSeqSet(...).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.

18
demandé sur W.P. McNeill 2012-12-04 08:59:12

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)
11
répondu som-snytt 2012-12-04 05:45:32

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.

10
répondu Owen 2012-12-04 05:23:29