Như Ganesh đã nói, bạn thực sự có thể sử dụng GADT để có độ an toàn cao hơn. Nhưng nếu bạn không muốn (hoặc cần), đây là việc tôi thực hiện việc này:
Như bạn đã biết, tất cả các thành phần của danh sách cần phải cùng loại. Nó không phải là rất hữu ích để có một danh sách các yếu tố của các loại khác nhau, bởi vì sau đó ném đi thông tin loại của bạn. Tuy nhiên, trong trường hợp này, vì bạn muốn loại bỏ thông tin loại (bạn chỉ quan tâm đến phần có thể rút được của giá trị), bạn nên thay đổi loại giá trị của mình thành một thứ có thể rút ra được.
type Drawable = IO()
shapes :: [Drawable]
shapes = [draw (Circle 5 10), draw (Circle 20 30), draw (Rectangle 10 15)]
Có lẽ, thực tế Drawable
sẽ được cái gì của bạn thú vị hơn chỉ IO()
(có lẽ cái gì đó như: MaxWidth -> IO()
).
Và cũng có thể, do đánh giá lười biếng, giá trị thực tế sẽ không được rút ra cho đến khi bạn buộc danh sách có thứ gì đó như sequence_
. Vì vậy, bạn không phải lo lắng về tác dụng phụ (nhưng bạn có thể đã thấy rằng từ loại shapes
).
Chỉ cần được đầy đủ (và kết hợp nhận xét của tôi vào câu trả lời này): Đây là một việc thực hiện tổng quát hơn, hữu ích nếu Shape
có nhiều chức năng:
type MaxWith = Int
class Shape a where
draw :: a -> MaxWidth -> IO()
size :: a -> Int
type ShapeResult = (MaxWidth -> IO(), Int)
shape :: (Shape a) => a -> ShapeResult
shape x = (draw x, size x)
shapes :: [ShapeResult]
shapes = [shape (Circle 5 10), shape (Circle 20 30), shape (Rectangle 10 15)]
Ở đây, shape
chức năng biến đổi một Shape a
giá trị thành một giá trị ShapeResult
, đơn giản bằng cách gọi tất cả các hàm trong lớp Shape
.Do lười biếng, không có giá trị nào thực sự được tính cho đến khi bạn cần chúng.
Thành thật mà nói, tôi không nghĩ rằng tôi thực sự sẽ sử dụng một cấu trúc như thế này. Tôi sẽ sử dụng phương pháp Drawable
từ trên cao, hoặc nếu cần một giải pháp tổng quát hơn, hãy sử dụng GADT. Điều đó đang được nói, đây là một bài tập thú vị.
Tôi không thể lấy ví dụ của bạn để biên dịch. Nhưng, trang wiki bạn tham khảo trả lời câu hỏi của tôi một cách hoàn hảo. –
Thử ngay bây giờ - Tôi đã có một 'Hình dạng', nơi tôi đã có một' IsShape' trong chữ ký của hàm tạo dữ liệu cho 'Hình dạng'. –
Cần chú ý đến độc giả trong tương lai rằng nhược điểm của phương pháp này là mã nhận các vòng tròn và hình chữ nhật ra khỏi thùng chứa 'Shape' sẽ không thể sử dụng * bất kỳ * thuộc tính nào khác so với những gì được đưa ra trong cá thể' IsShape'; bạn không thể nhận được tọa độ, không thể biết đó là 'Vòng tròn' hay 'Hình chữ nhật', và không thể gọi bất kỳ chức năng nào khác hoạt động đặc biệt trên hình tròn hoặc hình chữ nhật, thay vì bất kỳ hình dạng nào. Đó là một hệ quả cơ bản của sự cởi mở mà Ganesh đang nói đến (và xuất hiện trong lập trình OO được gõ tĩnh khi bạn cảm thấy bị buộc phải "downcast"). – Ben