Tại sao trình biên dịch Scala không thể đưa ra cảnh báo khớp mẫu cho các lớp / đặc điểm không được bảo mật?


10

Nếu tôi sử dụng một un kín traithoặc abstract classtrong Scala và sau đó sử dụng mô hình phù hợp, tôi tự hỏi, không trình biên dịch không biết tại thời gian biên dịch cho patternmatch đặc biệt này những gì thực hiện khác nhau của đặc điểm này / lớp có sẵn? Vì vậy, nếu có, nó có thể không đưa ra cảnh báo khớp mẫu hay không mặc dù trait/ abstract classkhông được niêm phong vì anh ta biết loại nào có thể được sử dụng, bằng cách kiểm tra tất cả các phụ thuộc / nhập khẩu có thể?

Ví dụ: nếu tôi có một Option[A]và tôi chỉ khớp mẫu cho Some[A]nhưng không cho None, trình biên dịch sẽ khiếu nại, vì đã Optionđược niêm phong.

Nếu trình biên dịch không thể biết / giải quyết điều đó, thì tại sao anh ta không thể? Và nếu trình biên dịch (về mặt lý thuyết) có thể làm điều đó, lý do nào khiến nó không được sử dụng trong Scala? Có ngôn ngữ nào khác hỗ trợ loại hành vi đó không?


Không rõ bạn đang hỏi gì. Bạn có muốn trình biên dịch phát ra cảnh báo nếu biểu thức khớp không bao gồm tất cả các đầu vào có thể không? Có lẽ một ví dụ sẽ làm cho câu hỏi của bạn rõ ràng hơn.
kdgregory

2
Nếu bất cứ ai có thể giới thiệu một lớp con mới, kết hợp mẫu không bao giờ có thể là toàn diện. Ví dụ: Bạn tạo một số lớp trừu tượng Foovới lớp con A, BC, và tất cả các trận đấu mẫu của bạn phù hợp với chỉ những ba. Không có gì ngăn tôi thêm một lớp con mới Dsẽ làm nổ tung các mẫu phù hợp của bạn.
Doval

@kdgregory Vâng, bạn hiểu rồi Tôi đã thêm một ví dụ để làm cho nó rõ ràng hơn.
valenterry

3
Kiểm tra tất cả các nhập khẩu là không đủ để khám phá tất cả các lớp con có thể. Một lớp con khác có thể được khai báo trong một tệp lớp riêng biệt được tải sau này trong thời gian chạy qua java.lang.ClassLoader.
amon

Câu trả lời:


17

Tìm ra tất cả các lớp con của một lớp được gọi là Phân tích phân cấp lớp và thực hiện CHA tĩnh trong một ngôn ngữ với tải mã động tương đương với việc giải quyết vấn đề dừng.

Thêm vào đó, một trong những mục tiêu của Scala là biên dịch và triển khai các mô-đun độc lập, vì vậy trình biên dịch đơn giản không thể biết liệu một lớp có được phân lớp trong một mô-đun khác hay không, bởi vì nó không bao giờ nhìn vào nhiều hơn một mô-đun. (Rốt cuộc, bạn có thể biên dịch một mô-đun dựa trên giao diện của một số mô-đun khác mà không có mô-đun đó thậm chí tồn tại trên hệ thống của bạn!) Đó là lý do tại sao sealedyêu cầu tất cả các lớp con phải được xác định trong cùng một đơn vị biên dịch.

Đó cũng là một trong những lý do tại sao các JVM có thể cạnh tranh thuận lợi với các trình biên dịch C ++: Trình biên dịch C ++ thường là các trình biên dịch tĩnh, do đó, chúng không thể tìm hiểu chung liệu một phương thức có bị ghi đè hay không và do đó không thể nội tuyến hóa nó. Các JVM OTOH, thường là các trình biên dịch động, chúng không cần thực hiện CHA để tìm hiểu xem một phương thức có bị ghi đè hay không, chúng chỉ có thể xem Phân cấp lớp trong thời gian chạy. Và ngay cả khi tại một thời điểm sau đó trong quá trình thực thi chương trình, một lớp con mới xuất hiện mà trước đó không có, không có vấn đề gì lớn, chỉ cần biên dịch lại đoạn mã đó mà không cần nội tuyến.

Lưu ý: tất cả điều này chỉ áp dụng trong Scala. JVM không có khái niệm nào sealed, vì vậy hoàn toàn có thể phân lớp sealedcác lớp từ một ngôn ngữ JVM khác, vì không có cách nào để truyền đạt ngôn ngữ này sang ngôn ngữ khác. Tài sealedsản được ghi lại trong ScalaSigchú thích, nhưng rõ ràng trình biên dịch của các ngôn ngữ khác không tính đến các chú thích đó.


3

có thể được thực hiện (ít nhất là cho tất cả các lớp được biết đến vào thời gian biên dịch), nó chỉ tốn kém. Bạn sẽ hủy hoàn toàn việc biên dịch gia tăng, bởi vì mọi thứ có chứa một mẫu phù hợp sẽ thực sự được biên dịch lại mỗi khi có bất kỳ tệp nào khác thay đổi.

Và bạn đang mua gì? Đó là một mùi mã để viết các mẫu khớp cần thay đổi thường xuyên khi một lớp dẫn xuất mới được thêm vào. Đó là một sự vi phạm nguyên tắc mở / đóng . Sử dụng kế thừa đúng cách và bạn sẽ không cần phải viết các loại khớp mẫu đó. Và đúng vậy, nguyên tắc mở / đóng cũng áp dụng cho các ngôn ngữ chức năng mà không cần kế thừa dựa trên lớp. Trong thực tế giữa các tính năng như các lớp loại, đa phương thức và chỉ các hàm bậc cao đơn giản hơn, các ngôn ngữ chức năng giúp việc mở rộng mà không cần sửa đổi dễ dàng hơn nhiều.


1
It can be done (at least for all classes known at compile time), it's just expensive.Nhưng nếu chương trình không độc lập 100% (nghĩa là nó phụ thuộc vào các .jartệp bên ngoài ), bạn có thể lẻn vào một lớp con mới sau khi biên dịch qua một trong các jars không? Vì vậy, trình biên dịch có thể cho bạn biết "Hiện tại các mẫu phù hợp của bạn đã hết, nhưng điều đó có thể thay đổi nếu bất kỳ phụ thuộc nào của bạn thay đổi" , điều này khá vô ích vì điểm có phụ thuộc bên ngoài là có thể cập nhật chúng mà không cần biên dịch lại!
Doval

Do đó từ chối trách nhiệm. Trong thực tế, nếu bạn đã kết hợp chặt chẽ đủ để cần một trận đấu toàn diện về sự phụ thuộc, bên ngoài hoặc nếu không, bạn sẽ muốn biên dịch lại bằng mọi cách.
Karl Bielefeldt

@Doval Sau đó, bạn nên sử dụng một số hình thức ủy quyền và để lớp được gọi quyết định những việc cần làm và đảo ngược điều khiển. Đây là những gì OOP có nghĩa là cho. Nếu bạn không muốn điều đó thì bạn có vấn đề hiện tại của bạn.
Sled

@ArtB Tôi không thấy ủy nhiệm hoặc đảo ngược điều khiển phải làm gì với việc này.
Doval

Nếu bạn muốn nó hoạt động với những người có thể thêm vào bên ngoài, thì hãy gọi lớp và di chuyển logic vào các lớp đó.
Sled
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.