2015-04-07 14 views
15

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 rFoo 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à:

  1. Tôi cảnh giác với người đứng đầu dụ chung cho Num trong mô-đun Foo.
  2. 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ần IncoherentInstances hoặc hạn chế Num trên foo để 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?

+3

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

+0

Đó là đầu số 'Num (c m r)' * nên * làm bạn sợ. – dfeuer

+0

Nhưng tôi nên làm gì *? :-) – crockeea

Trả lời

1

Sau 6 tháng suy nghĩ, cuối cùng tôi đã có câu trả lời cho bình luận lúng túng của tôi ở trên: thêm một wrapper newtype!

tôi chia lớp Cyc trong hai:

class Foo m 

class Cyc c where 
    cyc :: (Foo m, Foo m') => c m r -> c m' r 

class EntailCyc c where 
    entailCyc :: Tagged (c m r) ((Foo m, Num r) :- (Num (c m r))) 

Sau đó, tôi xác định Cyc dụ của tôi như trên:

data Bar m r = ... 

instance Cyc Bar where ... 

instance (Num r, Foo m) => Num (Bar m r) where ... 

instance EntailCyc Bar where 
    witNum _ = Dict 

Sau đó, tôi xác định một wrapper Newtype và đưa ra một Cyc dụ chung cho nó:

newtype W c m r = W (c m r) 

instance Cyc (W c m r) where cyc (W a) = W $ cyc a 

instance (EntailCyc c, Foo m, Num r) => Num (W c m r) where 
    (W a) + (W b) = a + b \\ witness entailCyc a 

Cuối cùng, tôi chan ge tất cả các chức năng mà sử dụng một generic c m r loại sử dụng một loại W c m r:

foo :: (Cyc c, EntailCyc c, Foo m, Num r) => W c m r -> W c m r 
foo = (*2) 

Vấn đề ở đây là foo có thể cần nhiều hạn chế (ví dụ, Eq (W c m r), Show (W c m r), vv) mà sẽ từng cá nhân yêu cầu riêng của họ ràng buộc. Tuy nhiên, các trường hợp chung cho W c m r cho Eq, Show, vv tất cả đều có chính xác những hạn chế (EntailCyc c, Foo m, Eq/Show/... a), do đó hạn chế về foo ở trên là chỉ trở ngại tôi cần phải viết!

Các vấn đề liên quan