Giả sử rằng tôi có hai kiểu dữ liệu Foo và Bar. Foo có các trường x và y. Thanh có các trường x và z. Tôi muốn có thể viết một hàm nhận Foo hoặc Bar làm tham số, trích xuất giá trị x, thực hiện một số phép tính trên nó, và sau đó trả về một Foo hoặc Bar mới với giá trị x được thiết lập tương ứng.Cú pháp ghi Haskell và loại lớp
Dưới đây là một cách tiếp cận:
class HasX a where
getX :: a -> Int
setX :: a -> Int -> a
data Foo = Foo Int Int deriving Show
instance HasX Foo where
getX (Foo x _) = x
setX (Foo _ y) val = Foo val y
getY (Foo _ z) = z
setY (Foo x _) val = Foo x val
data Bar = Bar Int Int deriving Show
instance HasX Bar where
getX (Bar x _) = x
setX (Bar _ z) val = Bar val z
getZ (Bar _ z) = z
setZ (Bar x _) val = Bar x val
modifyX :: (HasX a) => a -> a
modifyX hasX = setX hasX $ getX hasX + 5
Vấn đề là tất cả những getter và setter là đau đớn để viết, đặc biệt là nếu tôi thay Foo và Bar với các kiểu dữ liệu thực tế rằng có rất nhiều lĩnh vực.
Cú pháp ghi của Haskell mang đến một cách xác định tốt hơn các bản ghi này. Nhưng, nếu tôi cố gắng xác định các bản ghi như thế này
data Foo = Foo {x :: Int, y :: Int} deriving Show
data Bar = Foo {x :: Int, z :: Int} deriving Show
Tôi sẽ gặp lỗi khi nói rằng x được xác định nhiều lần. Và, tôi không thấy bất kỳ cách nào để làm cho một phần của một lớp loại để tôi có thể vượt qua chúng để sửa đổiX.
Có cách nào tốt đẹp để giải quyết vấn đề này không, hoặc tôi có bị mắc kẹt với việc xác định getters và setters của riêng mình không? Đặt một cách khác, là có một cách để kết nối các chức năng được tạo ra bởi cú pháp kỷ lục với các loại lớp (cả getters và setters)?
EDIT
Đây là vấn đề thực sự tôi đang cố giải quyết. Tôi đang viết một loạt các chương trình liên quan mà tất cả đều sử dụng System.Console.GetOpt để phân tích các tùy chọn dòng lệnh của họ. Sẽ có rất nhiều tùy chọn dòng lệnh phổ biến trên các chương trình này, nhưng một số chương trình có thể có các tùy chọn bổ sung. Tôi muốn mỗi chương trình có thể xác định một bản ghi chứa tất cả các giá trị tùy chọn của nó. Sau đó tôi bắt đầu với một giá trị bản ghi mặc định sau đó được chuyển đổi thông qua một đơn vị StateT và GetOpt để có được một bản ghi cuối cùng phản ánh các đối số dòng lệnh. Đối với một chương trình duy nhất, cách tiếp cận này hoạt động thực sự tốt, nhưng tôi đang cố gắng tìm cách sử dụng lại mã trên tất cả các chương trình.
Nếu bạn có một kiểu dữ liệu duy nhất 'dữ liệu FooBar = Foo {x :: Int, y :: Int} | Bar {x :: Int, z :: Int} 'bạn sẽ không gặp vấn đề này. Nếu các kiểu dữ liệu của bạn nằm trong các mô-đun khác nhau, bạn có thể sử dụng '{- # LANGUAGE DisambiguateRecordFields # -}'. Bất kỳ lý do gì để gắn bó với thiết kế hiện tại của bạn? – ephemient