Để nâng cao kiến thức về các phần mở rộng GHC, tôi quyết định thử và thực hiện các số với các đơn vị. Một thứ tôi muốn làm là sử dụng các chữ số cho các giá trị đơn vị. Nhưng điều đó hóa ra là không thực tế do giả định thế giới mở mà Haskell tạo ra. Một ví dụ nhỏ về những gì tôi không thể bắt đầu làm việc như sau:Làm thế nào để hạn chế giả định thế giới mở trong Haskell
data Unit u a = Unit a
data NoUnit
instance Num a => Num (Unit NoUnit a) where
-- (+) (*) (-) abs signum not important
fromInteger = Unit . fromInteger
type family Multiply a b where
Multiply NoUnit NoUnit = NoUnit
multiply :: Num a => Unit u1 a -> Unit u2 a -> Unit (Multiply u1 u2) a
multiply (Unit a) (Unit b) = Unit $ a * b
Bây giờ nếu tôi cố gắng và làm điều gì đó như multiply 1 1
tôi mong đợi giá trị kết quả là rõ ràng. Bởi vì cách duy nhất để có được thứ gì đó thuộc loại Num (Unit u a)
là bằng cách đặt u
thành NoUnit
. Và a
còn lại phải được xử lý theo các quy tắc mặc định.
Thật không may do giả định thế giới mở của Haskell có thể là một số người xấu đã quyết định rằng ngay cả một số có đơn vị phải là một phiên bản Num
hợp lệ mặc dù một điều như vậy sẽ vi phạm (*) :: a -> a -> a
như phép nhân số với đơn vị không phù hợp với chữ ký kiểu đó.
Bây giờ giả định thế giới mở không phải là một giả định không hợp lý, đặc biệt là vì các phiên bản trẻ mồ côi không bị báo cáo Haskell cấm. Nhưng trong trường hợp cụ thể này, tôi thực sự muốn nói với GHC rằng loại đơn vị ảo hợp lệ duy nhất cho một ví dụ Num
của Unit
là NoUnit
.
Có cách nào để nói rõ điều này, và về phần nào của một lưu ý phụ sẽ không cho phép trẻ mồ côi trường hợp cho phép GHC để dễ dàng lên trên giả định thế giới mở ở tất cả?
Loại điều này đã xuất hiện một vài lần khi cố gắng làm cho chương trình của tôi an toàn hơn với cách gõ phụ thuộc một phần. Bất cứ khi nào tôi muốn chỉ định một trường hợp Num
hoặc IsString
hoặc IsList
cho trường hợp cơ sở và sau đó sử dụng các giá trị hoặc chức năng tùy chỉnh để có được tất cả các trường hợp có thể khác.
Điều này không liên quan, nhưng tôi mong rằng gia đình bạn 'Multiply' sẽ có' Multiply NoUnit y = y' và 'Multiply x NoUnit = x' thay vì' Multiply NoUnit NoUnit = NoUnit'. Ngoài ra, Adam Gundry kết luận rằng các thiết bị tích hợp của GHC không đủ để làm việc cho các đơn vị đo lường tốt. Bạn nên kiểm tra [uom-plugin] (https://hackage.haskell.org/package/uom-plugin), một plugin kiểm tra kiểu hỗ trợ loại điều này. – dfeuer
@dfeuer Cảm ơn sự giúp đỡ nhưng mã thực tế của tôi không phải như thế này, đây chỉ là một ví dụ thất bại tối thiểu. Mã thực sự của tôi rất khác và sử dụng 'DataKinds'. Nó có vẻ làm việc cho tất cả các đơn vị SI một cách chính xác, bằng cách chỉ theo dõi bao nhiêu của mỗi đơn vị SI cơ sở 7 được kết hợp vào mỗi giá trị. – semicolon