Nhiều hơn hoặc ít hơn bất kỳ việc sử dụng các loại thành viên (tức là lồng nhau) có thể làm phát sinh nhu cầu về các loại phương thức phụ thuộc. Cụ thể, tôi duy trì rằng không có các kiểu phương thức phụ thuộc, kiểu bánh cổ điển gần giống với kiểu chống mẫu hơn.
Vậy vấn đề là gì? Các kiểu lồng nhau trong Scala phụ thuộc vào thể hiện kèm theo của chúng. Do đó, trong trường hợp không có các loại phương thức phụ thuộc, các nỗ lực sử dụng chúng bên ngoài trường hợp đó có thể gây khó khăn một cách khó chịu. Điều này có thể biến những thiết kế ban đầu có vẻ thanh lịch và lôi cuốn thành những thứ quái dị, cứng nhắc và khó tái cấu trúc.
Tôi sẽ minh họa rằng với một bài tập tôi đưa ra trong khóa đào tạo Advanced Scala của mình ,
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
}
Đó là một ví dụ về mô hình bánh cổ điển: chúng ta có một gia đình trừu tượng dần dần được tinh chế thông qua chế độ bá đạo ( ResourceManager
/ Resource
được tinh chế bởi FileManager
/ File
lần lượt được tinh chỉnh bởi NetworkFileManager
/RemoteFile
). Đây là một ví dụ về đồ chơi, nhưng mô hình là có thật: nó được sử dụng trong suốt trình biên dịch Scala và được sử dụng rộng rãi trong plugin Scala Eclipse.
Đây là một ví dụ về sự trừu tượng trong sử dụng,
val nfm = new NetworkFileManager
val rf : nfm.Resource = nfm.create
nfm.testHash(rf)
nfm.testDuplicates(rf)
Lưu ý rằng phụ thuộc đường dẫn có nghĩa là trình biên dịch sẽ đảm bảo rằng các phương thức testHash
và chỉ có thể được gọi với các đối số tương ứng với nó, tức là. nó là của riêng bạn , và không có gì khác.testDuplicates
NetworkFileManager
RemoteFiles
Không thể phủ nhận đó là một thuộc tính mong muốn, nhưng giả sử chúng tôi muốn chuyển mã kiểm tra này sang một tệp nguồn khác? Với các loại phương thức phụ thuộc, thật dễ dàng để xác định lại các phương thức đó bên ngoài ResourceManager
hệ thống phân cấp,
def testHash4(rm : ResourceManager)(r : rm.Resource) =
assert(r.hash == "9e47088d")
def testDuplicates4(rm : ResourceManager)(r : rm.Resource) =
assert(r.duplicates(r))
Lưu ý việc sử dụng các loại phương thức phụ thuộc ở đây: loại đối số thứ hai ( rm.Resource
) phụ thuộc vào giá trị của đối số thứ nhất ( rm
).
Có thể thực hiện việc này mà không cần các loại phương pháp phụ thuộc, nhưng nó cực kỳ lúng túng và cơ chế này khá không trực quan: Tôi đã dạy khóa học này gần hai năm nay và trong thời gian đó, không có ai đưa ra giải pháp làm việc chưa được thực hiện.
Hãy thử nó cho chính mình ...
// 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)
Sau một thời gian ngắn vật lộn với nó, có lẽ bạn sẽ khám phá ra lý do tại sao tôi (hoặc có thể là David MacIver, chúng tôi không thể nhớ ai trong số chúng tôi đặt ra thuật ngữ này) gọi đây là Bakery of Doom.
Chỉnh sửa: sự đồng thuận là Bakery of Doom là đồng tiền của David MacIver ...
Về phần thưởng: Hình thức các loại phụ thuộc của Scala nói chung (và các loại phương thức phụ thuộc như một phần của nó) được lấy cảm hứng từ ngôn ngữ lập trình Beta ... chúng phát sinh một cách tự nhiên từ ngữ nghĩa lồng nhau nhất quán của Beta. Tôi không biết bất kỳ ngôn ngữ lập trình chính thống thậm chí mờ nhạt nào khác có các loại phụ thuộc trong hình thức này. Các ngôn ngữ như Coq, Cayenne, Epigram và Agda có một kiểu gõ phụ thuộc khác, theo một cách nào đó chung chung hơn, nhưng khác biệt đáng kể bởi là một phần của các hệ thống loại, không giống như Scala, không có phân nhóm.