2014-02-05 6 views
0

Списки в Scala являются ковариантными (List[A+]). Я обнаружил, что это вызывает у меня больше проблем, чем что-либо еще, и я ищу способ применения инвариантности типов в своих списках. Далее следует дать ошибку компиляции:Как можно ввести инвариантность типа?

scala> val l: List[Int] = List(1, 2, 3) 
l: List[Int] = List(1, 2, 3) 

scala> "string" :: l 
res0: List[Any] = List(string, 1, 2, 3) 

scala> 1.0 :: l 
res1: List[AnyVal] = List(1.0, 1, 2, 3) 

Edit: Обратите внимание, что это сделано на примере, и я хотел бы знать, если есть универсальный решение, которое работает на всех Скале Seq, Set и Map, или даже любой признак, принимающий параметр типа. Если это невозможно, и единственный вариант - отказаться от коллекций Scala за что-то вроде scalaz или psp-view, то это ответ.

ответ

8

Можно указать тип результата:

val rescala> val result: List[Int] = "string" :: l 
<console>:8: error: type mismatch; 
found : String 
required: Int 
     val result: List[Int] = "string" :: l 
             ^

Вы также можете создавать свои собственные инвариантные методы, как это:

def prepend[T1, T2](t: T1, l: List[T2])(implicit e: T1 =:= T2) = e(t) :: l 

prepend(0, l) 
// List[Int] = List(0, 1, 2, 3) 

scala> prepend("str", l) 
<console>:10: error: Cannot prove that String =:= Int. 
       prepend("str", l) 
        ^

С value classes вы можете создать инвариантную оболочку для List без исполнения наказания, как это:

case class InvariantList[T](l: List[T]) extends AnyVal { 
    def ::(t: T) = InvariantList(t :: l) 
} 

val l = InvariantList(1 :: 2 :: 3 :: Nil) 

0 :: l 
// InvariantList(List(0, 1, 2, 3)) 

scala> "str" :: l 
<console>:13: error: type mismatch; 
found : String 
required: Int 
       "str" :: l 
        ^

Вы также можете использовать инвариантные методы из scalaz для коллекций конкатенации:

import scalaz._, Scalaz._ 

List(0) |+| List(1, 2, 3) 
// List(0, 1, 2, 3) 

Vector('a) |+| Vector('b, 'c) 
// Vector('a, 'b, 'c) 

scala> List("string") |+| List(1, 2, 3) 
<console>:14: error: type mismatch; 
found : Int(1) 
required: String 
       List("string") |+| List(1, 2, 3) 
            ^

Обратите внимание, что (как уже упоминалось @drexin) существует инвариантное список в scalaz: IList.

+0

Задание типа результата везде означает, что я отказываюсь от вывода типа. Обертывание/расширение всех методов scala.collection._ займет много времени, и мне нужно будет нести этот код во всех моих проектах ... – OlivierBlanvillain

+0

@OlivierBlanvillain: вам не нужно указывать тип результата везде. Вы можете просто указать его для результата всех методов и всех членов класса. Таким образом, вы получите ошибку компиляции. – senia

+0

Я всегда устанавливал типы возвращаемых данных, но мне все еще удалось написать багги код из-за неопределенности ковариации;) – OlivierBlanvillain

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