Kurang lebih setiap penggunaan tipe anggota (mis. Bersarang) dapat menimbulkan kebutuhan akan tipe metode dependen. Secara khusus, saya berpendapat bahwa tanpa jenis metode dependen pola kue klasik lebih dekat menjadi anti-pola.
Jadi apa masalahnya? Jenis bersarang di Scala tergantung pada contoh terlampir mereka. Akibatnya, dengan tidak adanya tipe metode dependen, upaya untuk menggunakannya di luar contoh itu bisa sangat sulit. Ini dapat mengubah desain yang awalnya tampak elegan dan menarik menjadi monster yang mengerikan dan sulit untuk diperbaiki.
Saya akan menggambarkan bahwa dengan latihan yang saya berikan selama kursus pelatihan Advanced Scala saya ,
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
}
Ini adalah contoh dari pola kue klasik: kami memiliki keluarga abstraksi yang secara bertahap disempurnakan melalui hierarki ( ResourceManager
/ Resource
disempurnakan oleh FileManager
/ File
yang pada gilirannya disempurnakan oleh NetworkFileManager
/ RemoteFile
). Ini contoh mainan, tetapi polanya nyata: digunakan di seluruh kompiler Scala dan digunakan secara luas di plugin Scala Eclipse.
Berikut adalah contoh abstraksi yang digunakan,
val nfm = new NetworkFileManager
val rf : nfm.Resource = nfm.create
nfm.testHash(rf)
nfm.testDuplicates(rf)
Perhatikan bahwa dependensi jalur berarti bahwa kompilator akan menjamin bahwa testHash
dan testDuplicates
metode NetworkFileManager
hanya dapat dipanggil dengan argumen yang sesuai dengannya, yaitu. itu sendiri RemoteFiles
, dan tidak ada yang lain.
Tidak dapat disangkal bahwa itu adalah properti yang diinginkan, tetapi misalkan kita ingin memindahkan kode uji ini ke file sumber yang berbeda? Dengan tipe metode dependen, mudah untuk mendefinisikan kembali metode-metode tersebut di luar ResourceManager
hierarki,
def testHash4(rm : ResourceManager)(r : rm.Resource) =
assert(r.hash == "9e47088d")
def testDuplicates4(rm : ResourceManager)(r : rm.Resource) =
assert(r.duplicates(r))
Perhatikan penggunaan tipe metode dependen di sini: jenis argumen kedua ( rm.Resource
) tergantung pada nilai argumen pertama ( rm
).
Dimungkinkan untuk melakukan ini tanpa tipe metode dependen, tetapi ini sangat canggung dan mekanismenya sangat tidak intuitif: Saya telah mengajar kursus ini selama hampir dua tahun sekarang, dan pada waktu itu, tidak ada seorang pun yang datang dengan solusi kerja tanpa alasan.
Cobalah sendiri ...
// 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)
Setelah beberapa saat berjuang dengan itu Anda mungkin akan menemukan mengapa saya (atau mungkin itu adalah David MacIver, kita tidak dapat mengingat siapa di antara kita yang menciptakan istilah) menyebut ini Bakery of Doom.
Sunting: konsensus adalah bahwa Bakery of Doom adalah koin David MacIver ...
Sebagai bonus: bentuk Scala dari tipe dependen secara umum (dan tipe metode dependen sebagai bagian dari itu) terinspirasi oleh bahasa pemrograman Beta ... mereka muncul secara alami dari semantik bersarang Beta yang konsisten. Saya tidak tahu bahasa pemrograman arus utama apa pun yang memiliki tipe dependen dalam formulir ini. Bahasa seperti Coq, Cayenne, Epigram dan Agda memiliki bentuk pengetikan dependen berbeda yang dalam beberapa hal lebih umum, tetapi berbeda secara signifikan dengan menjadi bagian dari sistem tipe yang, tidak seperti Scala, tidak memiliki subtyping.