Sorun, bu parametresinde Seq
'un eşanlamlıdır. Bu, işlevselliğinin büyük çoğunluğu için çok mantıklıdır. Değişken bir konteyner olarak, eşdeğeri olmalıdır. Ne yazık ki, bu, parametrelenmiş tipin bir kısmını alan bir yöntem tanımlamak zorunda kaldığında ortaya çıkıyor. Aşağıdaki örneği inceleyelim:
trait Seq[+A] {
def apply(i: Int): A // perfectly valid
def contains(v: A): Boolean // does not compile!
}
sorun fonksiyonları her zaman kendi dönüş türleri kendi parametre türleri ve kovaryant içinde kontravaryant olmasıdır. Böylece, apply
yöntemi A
türünde bir değer döndürebilir, çünkü A
, apply
için dönüş türüyle birlikte değişkendir. Bununla birlikte, , türünde A
türünde bir değer alamıyor, çünkü bu parametrenin kontravaryant olması gerekir.
Bu sorun, farklı şekillerde çözülebilir. Bir seçenek A
'u değişmez tip parametresi yapmaktır. Bu, hem kovaryant hem de kontravaryant bağlamlarda kullanılmasına izin verir. Bununla birlikte, bu tasarım Seq[String]
'un 'un Seq[Any]
alt tipi olması anlamına gelir. Diğer bir seçenek (ve en sık kullanılanı), aşağıda eşdeğer tipte sınırlanan bir yerel tip parametresi kullanmaktır. Örneğin: heterojen kapları kullanan bir kod yazarken bazı çok sezgisel sonuçlar sağlar olarak
trait Seq[+A] {
def +[B >: A](v: B): Seq[B]
}
Bu numara yanı Seq[String] <: Seq[Any]
özelliğini korur. Örneğin: Any
çünkü
val s: Seq[String] = ...
s + 1 // will be of type Seq[Any]
bu örnekte +
fonksiyonunun sonucu, tip Seq[Any]
bir değeri olan en üst sınır tip String
ve Int
için (LUB) (diğer bir deyişle, en az -komik süper tip). Bunu düşünürseniz, tam da beklediğimiz davranış budur.Hem String
hem de Int
bileşenleriyle bir dizi oluşturursanız, türüSeq[Any]
olmalıdır.
Maalesef bu hile, contains
gibi yöntemlerle uygulanabilir iken, bazı şaşırtıcı sonuçlar üretir:
trait Seq[+A] {
def contains[B >: A](v: B): Boolean // compiles just fine
}
val s: Seq[String] = ...
s contains 1 // compiles!
Buradaki sorun biz tip Int
değerinin iletilmesi contains
yöntemini çağırarak olmasıdır. Scala bunu görür ve Int
ve A
'un bir üst türü olan B
için bir tür çıkarmaya çalışır, bu durumda String
olarak başlatılır. Bu iki tür için LUB, Any
(daha önce gösterildiği gibi) ve contains
için yerel tür örneklemesi Any => Boolean
olacaktır. Böylece,yönteminin güvenli olmaması için görünür.
ikisi de kendi parametre türlerinde covariant çünkü Bu sonuç Map
veya Set
için bir sorun değildir:
trait Map[K, +V] {
def contains(key: K): Boolean // compiles
}
trait Set[A] {
def contains(v: A): Boolean // also compiles
}
Yani, uzun lafın kısası, kovaryant konteyner türlerinde contains
yöntem ile sınırlı tutulamaz Fonksiyon tiplerinin çalışma şeklinden dolayı sadece bileşen tipinin değerlerini alır (parametre tiplerinde kontravaryant). Bu gerçekten Scala veya kötü bir uygulama sınırlaması değildir, matematiksel bir gerçektir.
Teselli ödülü, bu gerçekten pratikte bir sorun değil. Ve diğer cevapların da belirttiği gibi, gerçekten gerçekten ek kontrolüne ihtiyacınız varsa, "tip-güvenli" contains
benzeri bir yöntem ekleyerek kendi örtük dönüşümünüzü tanımlayabilirsiniz. tip eşitlik için bir delil temin By
Derleyiciye tip gereksinimlerini karşılamak için yalnızca resmi olarak orada bulunan ve belirli bir bağlamda daraltılabilen parametreler hakkında bilgi veren bir mekanizmanın olmaması üzücü bir durumdur. Liste [B] 'nin sadece B'yi içerebileceği doğrudur, fakat aynı zamanda Liste [B]' nin Liste [A] olarak yeniden yorumlanabileceği doğrudur, burada B <: A ve daha sonra (yaklaşık) sorulabilir. –
Burada olan tam olarak budur. “Seq [String]' 'Seq [Any]' olarak yeniden yorumlanır ve Int'ten (: Any) sonra Int (Int) hakkında soru sorabiliriz. –
Bu, matematiksel bir olguya dönüşen matematiği görmeyi çok isterim (üstelik kontravaryant olan üsselerin ötesinde). –