Tôi thấy mình gặp sự cố thường gặp khi viết các chương trình lớn hơn trong Haskell. Tôi thấy mình thường muốn nhiều loại riêng biệt chia sẻ một đại diện nội bộ và một số hoạt động cốt lõi.Xử lý nhiều loại với cùng một biểu diễn nội bộ và bản mẫu nhỏ nhất?
Có hai cách tiếp cận tương đối rõ ràng để giải quyết vấn đề này.
Một người đang sử dụng loại lớp và tiện ích mở rộng GeneralizedNewtypeDeriving
. Đặt đủ logic vào một loại lớp để hỗ trợ các hoạt động được chia sẻ mà trường hợp sử dụng mong muốn. Tạo một kiểu với biểu diễn mong muốn và tạo một thể hiện của lớp kiểu cho kiểu đó. Sau đó, đối với từng trường hợp sử dụng, tạo trình bao bọc cho nó bằng newtype và lấy được lớp chung.
Cách khác là khai báo loại có biến kiểu ảo và sau đó sử dụng EmptyDataDecls
để tạo các loại riêng biệt cho từng trường hợp sử dụng khác nhau.
Mối quan tâm chính của tôi là không trộn lẫn các giá trị chia sẻ nội dung đại diện và hoạt động, nhưng có ý nghĩa khác nhau trong mã của tôi. Cả hai cách tiếp cận này giải quyết vấn đề đó, nhưng cảm thấy vụng về đáng kể. Mối quan tâm thứ hai của tôi là giảm số lượng boilerplate cần thiết, và cả hai cách tiếp cận đều làm tốt ở đó.
Những ưu điểm và nhược điểm của từng phương pháp tiếp cận là gì? Có một kỹ thuật đến gần hơn để làm những gì tôi muốn, cung cấp loại an toàn mà không có mã boilerplate?
Nếu bộ nhớ phục vụ cho tôi, 'dữ liệu Foo a = Foo a',' dữ liệu Foo ab = Foo a' và 'newtype Bar a = Thanh (Foo a)' (với 'Foo' đầu tiên) nên biên dịch thành đại diện thời gian chạy, do đó, việc tìm kiếm sự khác biệt không nhỏ trong hiệu suất sẽ hơi bất ngờ. –
@camccann Vẻ đẹp của ghc-core và Tiêu chí là bằng chứng thực nghiệm để bổ sung bộ nhớ! :) Tôi nghĩ rằng câu hỏi hiệu suất có nhiều hơn để làm với liệu các hoạt động đến từ một lớp tác động đến hiệu suất của họ như trái ngược với đại diện thời gian chạy của bản thân giá trị. Các hàm đa hình đi từ '(General a, General b) => a -> b -> Int' thành' General2 a -> General2 b -> Int'. – Anthony