2015-09-23 18 views
6

phép nói rằng tôi đã có lớp:lớp generalising thông số

class C a b t where 
    f :: (a, b) -> (t a, t b) 

Bây giờ với định nghĩa này, tôi có thể xác định các trường hợp cho tiếng nói:

(a,b) -> (Maybe a, Maybe b) 
(a,b) -> ([a], [b]) 

Nhưng không cho (như xa như tôi hiểu):

(a,b) -> (a,b) 
(a,b) -> ((a, a), (b, b)) 

tôi thay vì có thể thay đổi định nghĩa lớp của tôi như vậy:

type family T a b x 

class C a b where 
    f :: (a, b) -> (T a b a, T a b b) 

Điều này sẽ cho phép tôi thực hiện việc trên, nhưng sau đó tôi chỉ có thể khai báo một f cho mỗi ab.

Về cơ bản, tôi muốn có thể truyền một họ loại là t trong định nghĩa gốc, được xác định ngầm bởi trình kiểm tra loại nếu được biết. Tôi không muốn chỉ làm cho nó f :: (a, b) -> (c, d) như tôi muốn duy trì bất biến mà cả hai ab có cùng một điều thực hiện cho họ, vì vậy swap . f là cùng loại với f . swap. Tôi nghĩ rằng tôi có thể cần injective type families (từ GHC 8.0) nhưng tôi không chắc chắn. Nhưng có thể có một cách khác?

+1

Không trả lời: bạn có thể sử dụng 'Danh tính' trong trường hợp đầu tiên và một kiểu mới phù hợp trong trường hợp thứ hai (tôi không thể nghĩ ra bất kỳ chuẩn nào ngay bây giờ). Đó là một cách giải quyết thô lỗ, nhưng nó có thể chứng minh ít đau đớn hơn so với thủ thuật tiên tiến này dường như kêu gọi. Bằng cách này, "Tôi không muốn chỉ làm cho nó' f :: (a, b) -> (c, d) 'như tôi muốn duy trì bất biến ..." âm thanh rất giống với vấn đề * ống kính * ống kính là 'ống kính stab' [mặc dù bốn tham số phụ thuộc lẫn nhau] (http://comonad.com/reader/2012/mirrored-lenses) (xem phần" Tại sao nó là một gia đình ống kính? "của bài). – duplode

+0

Tôi nghĩ rằng đây là cơ bản cùng một câu hỏi như http://stackoverflow.com/questions/4922560/why-doesnt-typesynonyminstances-allow-partially-applied-type-synonyms-to-be-use –

Trả lời

5

Điều này có thể hoặc không thể trả lời câu hỏi của bạn tùy thuộc vào những gì bạn thực sự cần làm.

Giải pháp tiêu chuẩn là sử dụng loại mới để xác định các phiên bản mới. Ví dụ.

newtype Pair a = Pair (a, a) 
instance C a b Pair where 
    f = ... 

Tuy nhiên, điều này sẽ gây ra f có loại

f :: (a, b) -> (Pair a, Pair b) 

thay vì muốn

f :: (a, b) -> ((a, a), (b, b)) 

Sau đó có thể được phục hồi một cách nhàm chán:

f' :: (a, b) -> ((a, a), (b, b)) 
f' p = case f p of (Pair x, Pair y) -> (x, y) 

Hiện tại, viết các hàm "bộ điều hợp" chẳng hạn như f' cảm thấy dư thừa: sau khi tất cả chúng ta chỉ cần loại bỏ một trình bao bọc newtype, không thay đổi biểu diễn thời gian chạy. Tệ hơn nữa, điều này có thể không hiệu quả: hãy xem xét chuyển đổi [Pair a] thành [(a, a)]. Để làm điều đó, chúng tôi sẽ cần phải lập bản đồ trên toàn bộ danh sách, vì vậy chúng tôi trả O (n) làm về cơ bản không có gì.

Một thay thế hiệu quả có thể được xây dựng bằng safe coercions:

import Data.Coerce 

f'' :: forall a b. (a, b) -> ((a, a), (b, b)) 
f'' = coerce (f :: (a, b) -> (Pair a, Pair b)) 

Điều này cho phép việc sử dụng các newtype s để lái xe lựa chọn ví dụ, tuy nhiên loại bỏ họ khi họ nhận được trong cách.

Bây giờ, nếu mục đích của bạn thực sự là xác định các phiên bản mới mà không cần newtype s, điều này không giúp ích gì nhiều. Thay vào đó, nếu bạn muốn một cách đơn giản để loại bỏ các trình bao bọc newtype s khỏi các phương thức mẫu, nó có thể giúp bạn.

+0

'Data.Coerce' ngoại hình thật sự thú vị. Cảm ơn câu trả lời đó. –