2009-03-24 2 views
83

EDIT: переписан этот вопрос на основе оригинального ответаПочему неизменный набор Scala не ковариант в своем типе?

scala.collection.immutable.Set класса не ковариантен в параметре типа. Почему это?

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 
} 
+0

Стоит отметить, что 'Foo (s.toSet [CharSequence])' отлично компилируется. Метод toSet - это O (1) - он просто обертывает 'asInstanceOf'. –

+1

Обратите внимание, что 'foo (Set (« Hello »,« World »))' компилируется тоже на 2.10, так как Scala, похоже, может вывести правильный тип Set. Он не работает с неявными преобразованиями (http://stackoverflow.com/questions/23274033/implicit-definition-working-for-seq-but-not-for-set/). –

ответ

48

Set является инвариантным в своем параметре типа из-за концепции за множествами в качестве функций. Следующие подписи должны прояснить вещи немного:

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

Если Set были ковариантными A, метод apply будет не в состоянии принять параметр типа A вследствие контрвариации функций. Set потенциально может быть контравариантен в A, но это тоже вызывает вопросы, когда вы хотите сделать что-то вроде этого:

def elements: Iterable[A] 

Короче говоря, лучшее решением будет держать вещи инвариантны даже для неизменных структур данных. Вы заметите, что immutable.Map также инвариантен в одном из его параметров типа.

+4

Я предполагаю, что этот аргумент опирается на «концепцию за множествами как функции» - можно ли это расширить? Например, какие преимущества имеет «набор как функция», дайте мне, что «установить как коллекцию» нет? Стоит ли потерять использование этого ковариантного типа? –

+0

В свете вашего ответа вы бы (в общем) * не * использовали Set как тип, возвращаемый API?Обычно у меня была реализация службы, сохраняющая внутренний набор в Java, причем сам API-интерфейс службы обертывает это в коллекции. немодифицируется по методу доступа. –

+0

Я думаю, что задаю соответствующий вопрос –

5

EDIT: для тех, кто интересно, почему этот ответ немного, кажется, не по теме, это потому, что я (спрашивающий) модифицировали вопрос.

Вывод типа Scala достаточно хорош, чтобы понять, что вы хотите CharSequences, а не Strings в некоторых ситуациях. В частности, следующие работы для меня в 2.7.3:

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

О том, как создать immutable.HashSets непосредственно: нет. Как оптимизация реализации, неизменяемые. HashSets из менее чем 5 элементов не являются фактически экземплярами неизменяемых. HashSet. Это либо EmptySet, Set1, Set2, Set3, либо Set4. Этот класс подклассов неизменен. Устанавливается, но не является неизменным.HashSet.

+0

Вы правы; пытаясь упростить мой фактический пример, я сделал тривиальную ошибку :-( –

46

в http://www.scala-lang.org/node/9764 Одерски пишет:

«К вопросу о множествах, я считаю, что не Вариативность также является результатом реализации общих наборов реализованы в виде хэш-таблиц, которые не являются вариантные массивы. тип ключа. Я согласен, что это немного раздражающая нерегулярность ».

Таким образом, кажется, что все наши усилия, чтобы построить принципиальную причину этого было ошибочным :-)

+1

Но некоторые последовательности также реализованы с массивами, и все же 'Seq' является ковариантным ... я что-то упустил? –

+2

Это может быть тривиально решено путем хранения 'Array [Any]' внутри. – rightfold

+0

@ rightfold правильный. Там может быть разумная причина, но это не так. –

Смежные вопросы