2013-03-08 23 views
6

Bir veri yapısı uyguluyorum. Scala'nın standart koleksiyon özelliklerinden hiçbirini doğrudan karıştırmasa da, bir üretici fabrikada standart Scala koleksiyonları oluşturabilen to[Col[_]] yöntemini eklemek istiyorum.Bir eşdeğişim koleksiyonu için [Col [_]] `metodunun eklenmesi

Şimdi GenTraversableOnce kopyalanan bu, varsayalım:

trait Foo[+A] { 
    def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A]]): Col[A] 
} 

Bu error: covariant type A occurs in invariant position ile başarısız olur.

GenTraversableOnce bunu nasıl sağlayabilir? Kaynak kodunda, bir annotation.unchecked.uncheckedVariance ...

'u görebiliyorum. Bu, kirli bir numaraya benziyor. Eğer bu seviye normal olarak reddedilirse, bu nasıl güvenli olabilir ve uncheckedVariance ile nasıl kapatılabilir?

cevap

2

Varyans denetimi, tür denetimi ve atlamanın çok önemli bir parçasıdır ve çalışma zamanı türünde bir hataya neden olabilir. Burada geçersiz bir çalışma zamanı değeriyle doldurulmuş bir türü yazdırarak gösterebilirim. Yine de bir tür döküm istisnası ile çökmesini sağlayamadım. res : Col[String] baskılar ExactlyOne(()) ile Burada println(res)

import collection.generic.CanBuildFrom 
import collection.mutable.Builder 
import scala.annotation.unchecked.uncheckedVariance 

trait Foo[+A] { 
    def toCol[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uncheckedVariance]]): Col[A @uncheckedVariance] 
} 

object NoStrings extends Foo[String] { 
    override def toCol[Col[_]](implicit cbf: CanBuildFrom[Nothing, String, Col[String]]): Col[String] = { 
    val res : Col[String] = cbf().result 
    println("Printing a Col[String]: ") 
    println(res) 
    res 
    } 
} 

case class ExactlyOne[T](t : T) 

implicit def buildExactlyOne = new CanBuildFrom[Nothing, Any, ExactlyOne[Any]] { 
    def apply() = new Builder[Any, ExactlyOne[Any]] { 
    def result = ExactlyOne({}) 
    def clear = {} 
    def +=(x : Any) = this 
    } 
    def apply(n : Nothing) = n 
} 

val noStrings : Foo[Any] = NoStrings 
noStrings.toCol[ExactlyOne] 

. Ancak, ExactlyOne(()), tür hatası gösteren Col[String] türüne sahip değildir.

import collection.generic.CanBuildFrom 

trait Foo[+A] { 
    def to[R](implicit cbf: CanBuildFrom[Nothing, A, R]): R 
} 

class EnrichedFoo[A](foo : Foo[A]) { 
    def toCol[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A]]): Col[A] = 
    foo.to[Col[A]] 
} 

implicit def enrich[A](foo : Foo[A]) = new EnrichedFoo(foo) 

case class Bar[A](elem: A) extends Foo[A] { 
    def to[R](implicit cbf: CanBuildFrom[Nothing, A, R]): R = { 
    val b = cbf() 
    b += elem 
    b.result() 
    } 
} 

val bar1 = Bar(3) 
println(bar1.toCol[Vector]) 
2

Bu, tür sistemini çevrelemek ve varyans denetimini dikkate almamak için @uncheckedVariance ek açıklamasını kullanabileceğinden dolayı olabilir.

Basitçe import scala.annotation.unchecked.uncheckedVariance ve varyans özürlü kontrol istediğiniz türünü açıklama:

def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uncheckedVariance]]): Col[A @uncheckedVariance] 

daha eksiksiz bir açıklama in the related answer bakınız.

2

@ axel22 tarafından belirtilen diğer soruya verilen bağlantıyı okurum. Yine de, gerçek neden olarak görünmüyor (GenTraversableOnce, hem varyant hem de değişmez koleksiyonlar için işlev görmesine izin veriyor - ,, A kodeksindedir).

Örneğin, aşağıdaki typer coercing olmadan düzgün çalışır:

import collection.generic.CanBuildFrom 

trait Foo[+A] { 
    def to[A1 >: A, Col[_]](implicit cbf: CanBuildFrom[Nothing, A1, Col[A1]]): Col[A1] 
} 

case class Bar[A](elem: A) extends Foo[A] { 
    def to[A1 >: A, Col[_]](implicit cbf: CanBuildFrom[Nothing, A1, Col[A1]]): Col[A1]= { 
    val b = cbf() 
    b += elem 
    b.result() 
    } 
} 

Bu bence doğru imzası olacaktır. Ama elbette, bu çirkin olur:

val b = Bar(33) 
b.to[Int, Vector] 

Yani, @uncheckedVariance kullanımının benim yorumlanması to imzasında (üst sınır olarak) eleman tipini tekrarlamak zorunda kalmamak için oluşurdu.

Yine de yanıt vermiyor, ancak bir çalışma zamanı hatasıyla varyansın ihmal edilmesine neden olan bir durum hayal edebiliyor muyuz?

+1

Sen don: varyans kurallarına saygı değişmez yardımcı sınıfına bildirdiğinden özellikten diğerine dönüştürmek için örtük dönüştürme kullanırken biz özelliğin dışında değişmeyen kodu taşımak ve sadece eşdeğişkin bölümünü tutabilir ederken

sorunu çözmek için Gerçekten A 've' A1' ile ilgili. Bu da çalışır: [def [c] Col [_]] (kapalı cbf: CanBuildFrom [Hiçbir şey, A, Col [B]]): Col [B] '. Bu bile gereksiz bir şekilde kısıtlayıcı gözüküyor, sadece “Col [B]” şeklinin sonucuna izin veriyor. Bu daha genel ve basit görünüyor: [R] (kapalı cbf: CanBuildFrom [Hiçbir şey, A, R]): R'. – Rotsor

İlgili konular