Tại sao bạn cần loại cao hơn?


13

Một số ngôn ngữ cho phép cho các lớp học và chức năng với các thông số loại (như List<T>nơi Tcó thể là một kiểu bất kỳ). Ví dụ: bạn có thể có một chức năng như:

List<S> Function<S, T>(List<T> list)

Tuy nhiên, một số ngôn ngữ cho phép khái niệm này được mở rộng thêm một cấp, cho phép bạn có chức năng với chữ ký:

K<S> Function<K<_>, S, T>(K<T> arg)

Trong K<_>đó chính nó là một loại như thế List<_>có một tham số loại. "Kiểu một phần" này được gọi là hàm tạo kiểu.

Câu hỏi của tôi là, tại sao bạn cần khả năng này? Nó có ý nghĩa để có một loại như List<T>bởi vì tất cả List<T>gần như giống hệt nhau, nhưng tất cả K<_>có thể hoàn toàn khác nhau. Bạn có thể có một Option<_>List<_>không có chức năng chung nào cả.


7
Có một số câu trả lời hay ở đây: stackoverflow.com/questions/21170493/ từ
itbruce 17/03/2015

3
@itsbruce Đặc biệt, Functorví dụ trong câu trả lời của Luis Casillas khá trực quan. Làm gì List<T>Option<T>có điểm gì chung? Nếu bạn cho tôi một hoặc một chức năng T -> Stôi có thể cung cấp cho bạn một List<S>hoặc Option<S>. Một điểm chung khác của họ là bạn có thể cố gắng để có được một Tgiá trị từ cả hai.
Doval 17/03/2015

@Doval: Bạn sẽ làm thế nào trước đây? Trong phạm vi mà một người quan tâm đến cái sau, tôi nghĩ rằng điều đó có thể được xử lý bằng cách thực hiện cả hai loại IReadableHolder<T>.
17/03/2015

@supercat Tôi đoán họ sẽ phải thực hiện IMappable<K<_>, T>với phương thức K<S> Map(Func<T, S> f), thực hiện như IMappable<Option<_>, T>, IMappable<List<_>, T>. Vì vậy, bạn sẽ phải hạn chế K<T> : IMappable<K<_>, T>để sử dụng nó.
GregRos 17/03/2015

1
Đúc không phải là một tương tự thích hợp. Điều được thực hiện với các lớp kiểu (hoặc các cấu trúc tương tự) là bạn chỉ ra cách một kiểu đã cho thỏa mãn kiểu được đánh giá cao hơn. Điều này thường liên quan đến việc xác định các hàm mới hoặc hiển thị các hàm hiện có (hoặc phương thức nếu ngôn ngữ OO như Scala) có thể được sử dụng. Một khi điều này đã được thực hiện, mọi chức năng được xác định để làm việc với loại cao hơn đều sẽ hoạt động với loại đó. Nhưng nó không chỉ là một giao diện vì nhiều hơn một tập hợp chữ ký hàm / phương thức đơn giản được xác định. Tôi đoán tôi sẽ phải đi và viết một câu trả lời để cho thấy cách thức hoạt động.
itbruce 17/03/2015

Câu trả lời:


5

Vì không có ai trả lời câu hỏi, tôi nghĩ tôi sẽ tự mình giải quyết. Tôi sẽ phải có một chút triết lý.

Lập trình chung là tất cả về trừu tượng hóa các loại tương tự, mà không làm mất thông tin loại (đó là những gì xảy ra với đa hình giá trị hướng đối tượng). Để làm điều này, các loại nhất thiết phải chia sẻ một số loại giao diện (một tập hợp các hoạt động, không phải là thuật ngữ OO) mà bạn có thể sử dụng.

Trong các ngôn ngữ hướng đối tượng, các kiểu thỏa mãn một giao diện nhờ các lớp. Mỗi lớp có giao diện riêng, được định nghĩa là một phần của loại. Vì tất cả các lớp List<T>chia sẻ cùng một giao diện, bạn có thể viết mã hoạt động bất kể Tbạn chọn loại nào. Một cách khác để áp đặt một giao diện là một ràng buộc kế thừa, và mặc dù cả hai có vẻ khác nhau, chúng giống nhau nếu bạn nghĩ về nó.

Trong hầu hết các ngôn ngữ hướng đối tượng, List<>bản thân nó không phải là một loại thích hợp. Nó không có phương thức, và do đó không có giao diện. Chỉ có List<T>điều đó có những điều này. Về cơ bản, về mặt kỹ thuật, các loại duy nhất bạn có thể trừu tượng hóa một cách có ý nghĩa là những loại có loại *. Để sử dụng các loại loại cao hơn trong một thế giới hướng đối tượng, bạn phải ràng buộc các cụm từ theo cách phù hợp với hạn chế này.

Ví dụ, như đã đề cập trong các nhận xét, chúng tôi có thể xem Option<>List<>dưới dạng "có thể ánh xạ", theo nghĩa là nếu bạn có chức năng, bạn có thể chuyển đổi Option<T>thành một Option<S>hoặc List<T>thành một List<S>. Hãy nhớ rằng các lớp không thể được sử dụng để trừu tượng trực tiếp trên các loại loại cao hơn, thay vào đó chúng ta tạo một giao diện:

IMappable<K<_>, T> where K<T> : IMappable<K<_>, T>

Và sau đó chúng tôi thực hiện giao diện trong cả hai List<T>Option<T>như IMappable<List<_>, T>IMappable<Option<_>, T>tương ứng. Những gì chúng tôi đã làm, là sử dụng các loại loại cao hơn để đặt các ràng buộc trên các loại thực tế (không phải loại cao hơn) Option<T>List<T>. Đây là cách nó được thực hiện trong Scala, mặc dù tất nhiên Scala có các tính năng như đặc điểm, biến loại và các tham số ngầm làm cho nó biểu cảm hơn.

Trong các ngôn ngữ khác, có thể trừu tượng hóa trực tiếp trên các loại loại cao hơn. Trong Haskell, một trong những cơ quan quyền lực cao nhất trên các hệ thống loại, chúng ta có thể cụm từ một loại loại cho bất kỳ loại nào, ngay cả khi nó có loại cao hơn. Ví dụ,

class Mappable mp where
    map :: mp a -> mp b

Đây là một ràng buộc được đặt trực tiếp trên một loại (không xác định) mpcó một tham số loại và yêu cầu nó được liên kết với hàm mapbiến một mp<a>thành một mp<b>. Sau đó, chúng ta có thể viết các hàm ràng buộc các loại loại cao hơn Mappablegiống như trong các ngôn ngữ hướng đối tượng, bạn có thể đặt một ràng buộc kế thừa. Vâng, loại.

Tóm lại, khả năng sử dụng các loại loại cao hơn của bạn phụ thuộc vào khả năng của bạn để hạn chế chúng hoặc sử dụng chúng như một phần của các ràng buộc loại.


Đã quá bận rộn để thay đổi công việc nhưng cuối cùng sẽ tìm thấy một chút thời gian để viết câu trả lời của riêng tôi. Tuy nhiên, tôi nghĩ rằng bạn đã bị phân tâm bởi ý tưởng về các ràng buộc. Một khía cạnh quan trọng của các loại được phân loại cao hơn là chúng cho phép các chức năng / hành vi được xác định có thể được áp dụng cho bất kỳ loại nào có thể được hiển thị một cách hợp lý để đủ điều kiện. Khác với các ràng buộc áp đặt đối với các loại hiện có, các lớp loại (một ứng dụng của các loại được phân loại cao hơn) thêm hành vi cho chúng.
itbruce

Tôi nghĩ đó là một vấn đề ngữ nghĩa. Một lớp loại định nghĩa một lớp các loại. Khi bạn xác định một hàm như (Mappable mp) => mp a -> mp b, bạn đã đặt một ràng buộc mpđể trở thành thành viên của lớp loại Mappable. Khi bạn khai báo một loại như là Optionmột thể hiện của Mappable, bạn thêm hành vi vào loại đó. Tôi đoán bạn có thể sử dụng hành vi đó cục bộ mà không bao giờ ràng buộc bất kỳ loại nào, nhưng sau đó không khác gì định nghĩa một hàm thông thường.
GregRos

Ngoài ra, tôi khá chắc chắn các lớp loại không liên quan trực tiếp đến các loại loại cao hơn. Scala có các loại loại cao hơn, nhưng không phải loại lớp và bạn có thể hạn chế các loại loại với loại *mà không làm cho chúng không sử dụng được. Mặc dù điều chắc chắn là các lớp loại rất mạnh khi làm việc với các loại loại cao hơn.
GregRos

Scala có các lớp loại. Chúng được thực hiện với ngụ ý. Các hàm ý cung cấp tương đương với các thể hiện của lớp loại Haskell. Đó là một triển khai dễ vỡ hơn Haskell vì không có cú pháp dành riêng cho nó. Nhưng nó luôn là một trong những mục đích chính của Scala và họ thực hiện công việc.
itbruce
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.