Scala'nın değişmez Seti neden türünde eşdeğişken değil?


95

DÜZENLEME : Bu soruyu orijinal cevaba göre yeniden yazdı

scala.collection.immutable.SetSınıf kendi türü parametresinde covariant değil. Bu neden?

import scala.collection.immutable._

def foo(s: Set[CharSequence]): Unit = {
    println(s)
}

def bar(): Unit = {
   val s: Set[String] = Set("Hello", "World");
   foo(s); //DOES NOT COMPILE, regardless of whether type is declared 
           //explicitly in the val s declaration
}

İyi foo(s.toSet[CharSequence])derlediğini belirtmekte fayda var . toSetSadece sarar - yöntem O (1) olup asInstanceOf.
john sullivan

1
Ayrıca, foo(Set("Hello", "World"))Scala doğru Set tipini çıkarabileceği için 2.10'da da derleneceğini unutmayın . Yine de örtük dönüştürmelerle çalışmaz ( stackoverflow.com/questions/23274033/… ).
LP_

Yanıtlar:


55

Setişlev olarak kümelerin arkasındaki kavram nedeniyle tür parametresinde değişmezdir. Aşağıdaki imzalar işleri biraz açıklığa kavuşturmalıdır:

trait Set[A] extends (A=>Boolean) {
  def apply(e: A): Boolean
}

Eğer Setkovaryant içinde olsaydı A, applyyöntem A, işlevlerin kontravaryansı nedeniyle bir tür parametresi alamazdı. Setpotansiyel olabilir kontravaryant içinde A, ama bu gibi şeyler yapmak istediğinizde bu çok sorunları neden olur:

def elements: Iterable[A]

Kısacası, en iyi çözüm, değişmez veri yapısı için bile her şeyi değişmeden tutmaktır. Bunun immutable.Maptür parametrelerinden birinde de değişmediğini fark edeceksiniz .


4
Sanırım bu argüman "işlevler olarak kümelerin arkasındaki kavram" etrafında dönüyor - bu genişletilebilir mi? Örneğin, "bir işlev olarak bir küme" nin hangi avantajları bana bir "koleksiyon olarak küme" nin vermemesini sağlar? Bu kovaryant türün kullanımını kaybetmeye değer mi?
oxbow_lakes

23
Tip imzası oldukça zayıf bir örnektir. Bir kümenin "uygula" sı, yöntemini içermesiyle aynı şeydir. Ne yazık ki, Scala'nın Listesi eş değişkendir ve bir de içerme yöntemine sahiptir. List'in içerdiği imzası elbette farklıdır, ancak yöntem aynı Set'inki gibi çalışır. Dolayısıyla, bir tasarım kararı dışında Set'in eş değişken olmasını gerçekten engelleyen hiçbir şey yoktur.
Daniel C. Sobral

6
Kümeler, matematiksel bir bakış açısıyla mantıksal işlevler değildir. Kümeler, Zermelo-Fraenkel aksiyomlarından "oluşturulmuştur", bazı dahil etme işlevi ile azaltılmamıştır . Bunun arkasındaki sebep Russell'ın paradoksudur: Eğer herhangi bir şey bir setin üyesi olabiliyorsa, o zaman kendilerinin üyesi olmayan Set R setlerini düşünün. O zaman soruyu sorun, R, R'nin bir üyesi mi?
oxbow_lakes

12
Kovaryans feda etmenin Set için buna değeceğine hâlâ ikna olmuş değilim. Elbette, bunun bir yüklem olması güzel, ancak genellikle biraz daha ayrıntılı olabilir ve "set" yerine "set.contains" kullanabilirsiniz (ve muhtemelen "set.contains" birçok durumda daha iyi okur).
Matt R

4
@Martin: List'in içerme yöntemi A'yı değil, Herhangi birini aldığından, List(1,2,3).contains _is (Any) => Booleantürü, türü Set(1,2,3).contains _ise res1: (Int) => Boolean.
Seth Tisue

52

En http://www.scala-lang.org/node/9764 Martin Odersky yazıyor:

"Kümeler konusunda, varyanssızlığın da uygulamalardan kaynaklandığına inanıyorum. Ortak kümeler, anahtar türünün değişken olmayan dizileri olan hashtables olarak uygulanır. Bunun biraz can sıkıcı bir düzensizlik olduğuna katılıyorum."

Öyleyse, bunun için ilkeli bir neden oluşturmak için tüm çabalarımız yanlış yönlendirilmiş gibi görünüyor :-)


1
Ancak bazı diziler aynı zamanda dizilerle de uygulanmaktadır ve yine Seqde kovaryanttır ... Bir şeyi mi kaçırıyorum?
LP_

4
Bu, Array[Any]dahili olarak depolayarak önemsiz bir şekilde çözülebilir .
sağ sayfa

@rightfold doğru. Makul bir nedeni olabilir, ama bu o değil.
Paul Draper

6

DÜZENLEME : Bu cevabın neden biraz konu dışı göründüğünü merak eden herkes için, bunun nedeni ben (soru soran) soruyu değiştirmiş olmam.

Scala'nın tür çıkarımı, bazı durumlarda Dizeleri değil de CharSequences'i istediğinizi anlamaya yetecek kadar iyidir. Özellikle aşağıdaki 2.7.3'te benim için çalışıyor:

import scala.collections.immutable._
def findCharSequences(): Set[CharSequence] = Set("Hello", "World")

İmmutable.HashSets'in nasıl doğrudan oluşturulacağına gelince: yapma. Bir uygulama optimizasyonu olarak, 5 öğeden daha az olan immutable.HashSets, aslında immutable.HashSet örnekleri değildir. Bunlar EmptySet, Set1, Set2, Set3 veya Set4'tür. Bu sınıflar alt sınıf immutable.Set, ancak immutable.HashSet değil.


Haklısın; gerçek örneğimi basitleştirmeye çalışırken önemsiz bir hata yaptım :-(
oxbow_lakes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.