Tôi có một lớp Cyc c r
có chức năng cho dữ liệu của biểu mẫu c m r
, trong đó m
là loại ma. Ví dụ:Biến Dict thành một ràng buộc
class Cyc c r where
cyc :: (Foo m, Foo m') => c m r -> c m' r
Tôi có lý do chính đáng để không làm m
thông số lớp học. Với mục đích của ví dụ này, lý do chính là nó làm giảm số lượng các ràng buộc về các hàm. Trong ví dụ thực tế của tôi, một nhu cầu hấp dẫn hơn cho giao diện này là tôi làm việc với các kiểu phantom thay đổi và ẩn, vì vậy giao diện này cho phép tôi có được ràng buộc Cyc
đối với bất kỳ loại ma nào.
Một nhược điểm của lựa chọn đó là tôi không thể thực hiện Num (c m r)
ràng buộc siêu lớp của Cyc
. Ý định của tôi là c m r
phải là Num
bất cứ khi nào (Cyc c r, Foo m)
. Giải pháp hiện tại rất khó chịu: Tôi đã thêm phương thức vào lớp Cyc
witNum :: (Foo m) => c m r -> Dict (Num (c m r))
loại sắp xếp nào cũng giống như vậy. Bây giờ khi tôi có một chức năng mà phải mất một generic Cyc
và cần có một chế Num (c m r)
, tôi có thể viết:
foo :: (Cyc c r, Foo m) => c m r -> c m r
foo c = case witNum c of
Dict -> c*2
Trong khóa học tôi thể thêm một hạn chế Num (c m r)
-foo
, nhưng tôi đang cố gắng để giảm số lượng của các ràng buộc, nhớ không? (Cyc c r, Foo m)
được cho là ngụ ý ràng buộc Num (c m r)
(và tôi cần Cyc c r
và Foo m
cho các mục đích khác), vì vậy tôi cũng không muốn phải viết ra ràng buộc Num
.
Trong quá trình viết câu hỏi này, tôi đã tìm ra cách tốt hơn (?) Để thực hiện điều này, nhưng nó có những hạn chế riêng.
Mô-đun Foo:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-}
module Foo where
import Data.Constraint
class Foo m
class Cyc c r where
cyc :: (Foo m, Foo m') => c m r -> c m' r
witNum :: (Foo m) => c m r -> Dict (Num (c m r))
instance (Foo m, Cyc c r) => Num (c m r) where
a * b = case witNum a of
Dict -> a * b
fromInteger a = case witNum (undefined :: c m r) of
Dict -> fromInteger a
-- no Num constraint and no Dict, best of both worlds
foo :: (Foo m, Cyc c r) => c m r -> c m r
foo = (*2)
Mô-đun Bar:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, OverlappingInstances #-}
module Bar where
import Foo
import Data.Constraint
data Bar m r = Bar r deriving (Show)
instance (Num r) => Cyc Bar r where
witNum _ = Dict
instance (Num r, Foo m) => Num (Bar m r) where
(Bar a) * (Bar b) = Bar $ a*b
fromInteger = Bar . fromInteger
instance Foo()
bar :: Bar() Int
bar = foo 3
Trong khi phương pháp này được tôi tất cả mọi thứ tôi đang tìm kiếm, có vẻ như mong manh. Mối quan tâm chính của tôi là:
- Tôi cảnh giác với người đứng đầu dụ chung cho
Num
trong mô-đunFoo
. - Nếu bất kỳ trường hợp trùng lặp nào được nhập vào
Foo
, tôi đột nhiên cầnIncoherentInstances
hoặc hạn chếNum
trênfoo
để trì hoãn lựa chọn mẫu để chạy.
Có cách nào khác để tránh sử dụng Dict
trong mọi chức năng cần để tránh một trong những nhược điểm này?
Nếu bạn đang sử dụng [ràng buộc] (https://hackage.haskell.org/package/constraints) cách thành ngữ để xác định 'witNum' sẽ là' witNum :: Foo m: - Num (cmr) 'mà bạn deconstruct với ví dụ 'foo = case witNum :: Foo m: - Số (c m r) của Sub Dict -> (* 2)' (không đẹp hơn). – Cirdec
Đó là đầu số 'Num (c m r)' * nên * làm bạn sợ. – dfeuer
Nhưng tôi nên làm gì *? :-) – crockeea