Üye (yani, iç içe geçmiş) türlerin az çok herhangi bir kullanımı, bağımlı yöntem türlerine ihtiyaç doğurabilir. Özellikle, bağımlı yöntem türleri olmadan klasik pasta modelinin bir anti-model olmaya daha yakın olduğunu düşünüyorum.
Peki sorun nedir? Scala'daki yuvalanmış türler, kapsayıcı örneklerine bağlıdır. Sonuç olarak, bağımlı yöntem türlerinin yokluğunda, bunları bu örneğin dışında kullanma girişimleri sinir bozucu şekilde zor olabilir. Bu, başlangıçta zarif ve çekici görünen tasarımları kabus gibi sert ve yeniden düzenlemesi zor canavarlara dönüştürebilir.
Benim sırasında vermek bir egzersiz ile bu açıklayacağız Gelişmiş Scala eğitim kursu ,
trait ResourceManager {
type Resource <: BasicResource
trait BasicResource {
def hash : String
def duplicates(r : Resource) : Boolean
}
def create : Resource
// Test methods: exercise is to move them outside ResourceManager
def testHash(r : Resource) = assert(r.hash == "9e47088d")
def testDuplicates(r : Resource) = assert(r.duplicates(r))
}
trait FileManager extends ResourceManager {
type Resource <: File
trait File extends BasicResource {
def local : Boolean
}
override def create : Resource
}
class NetworkFileManager extends FileManager {
type Resource = RemoteFile
class RemoteFile extends File {
def local = false
def hash = "9e47088d"
def duplicates(r : Resource) = (local == r.local) && (hash == r.hash)
}
override def create : Resource = new RemoteFile
}
Bu, klasik pasta modelinin bir örneğidir: bir hiyerarşi aracılığıyla kademeli olarak rafine edilen ( ResourceManager
/ Resource
tarafından rafine edilen FileManager
/ File
daha sonra NetworkFileManager
/ tarafından rafine edilen RemoteFile
) bir soyutlama ailesine sahibiz . Bu bir oyuncak örneği, ancak desen gerçektir: Scala derleyicisinin tamamında kullanıldı ve Scala Eclipse eklentisinde yoğun bir şekilde kullanıldı.
İşte kullanımdaki soyutlamanın bir örneği:
val nfm = new NetworkFileManager
val rf : nfm.Resource = nfm.create
nfm.testHash(rf)
nfm.testDuplicates(rf)
Yol bağımlılığının, derleyicinin testHash
ve testDuplicates
yöntemlerinin NetworkFileManager
yalnızca ona karşılık gelen argümanlarla çağrılabileceğini garanti edeceği anlamına geldiğine dikkat edin . kendi RemoteFiles
ve başka bir şey değil.
Bu inkar edilemez bir şekilde arzu edilen bir özellik, ancak bu test kodunu farklı bir kaynak dosyaya taşımak istediğimizi varsayalım. Bağımlı yöntem türleri ile bu yöntemleri ResourceManager
hiyerarşinin dışında yeniden tanımlamak oldukça kolaydır ,
def testHash4(rm : ResourceManager)(r : rm.Resource) =
assert(r.hash == "9e47088d")
def testDuplicates4(rm : ResourceManager)(r : rm.Resource) =
assert(r.duplicates(r))
Burada bağımlı yöntem türlerinin kullanımlarına dikkat edin: ikinci argümanın ( rm.Resource
) türü ilk argümanın ( rm
) değerine bağlıdır .
Bağımlı yöntem türleri olmadan bunu yapmak mümkündür, ancak bu son derece garip ve mekanizma oldukça mantıksız: Bu kursu yaklaşık iki yıldır öğretiyorum ve bu süre zarfında kimse istenmeden çalışan bir çözüm bulamadı.
Kendiniz deneyin ...
// Reimplement the testHash and testDuplicates methods outside
// the ResourceManager hierarchy without using dependent method types
def testHash // TODO ...
def testDuplicates // TODO ...
testHash(rf)
testDuplicates(rf)
Kısa bir süre onunla mücadele ettikten sonra muhtemelen neden benim (veya belki de David MacIver idi, hangimizin bu terimi icat ettiğini hatırlayamıyoruz) buna Doom Fırını diyoruz.
Düzenleme: Fikir birliği, Bakery of Doom'un David MacIver'ın madeni parası olduğu yönünde ...
Bonus için: Scala'nın genel olarak bağımlı türler biçimi (ve bunun bir parçası olarak bağımlı yöntem türleri) programlama dili Beta'dan esinlenmiştir ... Doğal olarak Beta'nın tutarlı yuvalama anlamından doğarlar. Bu biçimde bağımlı türlere sahip olan, hatta biraz da yaygın olan başka bir programlama dili bilmiyorum. Coq, Cayenne, Epigram ve Agda gibi diller, bazı yönlerden daha genel olan, ancak Scala'dan farklı olarak alt tipleme içermeyen tip sistemlerin parçası olarak önemli ölçüde farklılık gösteren farklı bir bağımlı yazım biçimine sahiptir.