2015-03-13 17 views
7

Tôi đang viết một CRUD như ứng dụng và có rất nhiều tra cứu bằng khóa chính (khóa chính có thể có các loại khác nhau). Vì vậy, tôi định nghĩa sau typeclass:Inferring Eq typeclass

{-# LANGUAGE MultiParamTypeClasses #-} 

class Eq b => HasPK a b where 
    getPK :: a -> b 

Bây giờ tôi có thể viết:

import Data.Maybe 

lookupPK :: HasPK a b => b -> [a] -> Maybe a 
lookupPK s = listToMaybe . filter ((== s) . getPK) 

Bây giờ, khi tôi muốn so sánh hai điều với PK, tôi chỉ muốn so sánh của PK của họ. Vì vậy, tôi đang cố gắng để xác định điều này:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 

instance (HasPK a b) => Eq a where 
    (==) = (==) `on` getPK 

Nhưng bây giờ nó mang lại cho tôi:

src/Utils.hs:61:10: Could not deduce (HasPK a b0) … 
     arising from the ambiguity check for an instance declaration 
    from the context (HasPK a b) 
     bound by an instance declaration: HasPK a b => Eq a 
     at /home/utdemir/workspace/.../Utils.hs:61:10-28 
    The type variable ‘b0’ is ambiguous 
    In the ambiguity check for: forall a b. HasPK a b => Eq a 
    To defer the ambiguity check to use sites, enable AllowAmbiguousTypes 
    In the instance declaration for ‘Eq a’ 
Compilation failed. 

bất cứ ai có thể giải thích lỗi này với tôi? Tôi đang đi đúng hướng hay có cách nào an toàn hơn để đạt được những gì tôi muốn?

Trả lời

9

Bạn cần một sự phụ thuộc chức năng ở đây: sử dụng

class Eq b => HasPK a b | a -> b where 
    getPK :: a -> b 

và cho phép các điểm bất kỳ phần mở rộng GHC tới.

Vấn đề này nằm trong khả năng của việc có nhiều PKS cho cùng loại, như trong

instance HasPK MyType Int where ... 
instance HasPK MyType String where ... 

Kể từ khi dụ đôi trên có thể được thêm vào sau, mã như (==) `on` getPK là có khả năng mơ hồ. Thật vậy, khi các trường hợp ở trên được thêm vào, nó không chỉ định có sử dụng PK Int hoặc String hay không.


Tuy nhiên, một thể hiện như

instance (HasPK a b) => Eq a where 
    ... 

có thể gây ra vấn đề, vì nó trùng lặp với bất kỳ Eq dụ khác. Hãy cẩn thận ở đây. Tôi thay vào đó sẽ viết

equalPK :: HasPK a b => a -> a -> Bool 
equalPK = (==) `on` getPK 

và sau đó cung cấp các trường hợp rõ ràng cho tất cả các loại có liên quan như thế này:

instance Eq MyType1 where (==) = equalPK 
instance Eq MyType2 where (==) = equalPK 
... 

Như thay thế khác, bạn có thể sử dụng một gia đình kiểu như

class HasPK a where 
    type PK a 
    getPK :: a -> PK a 

equalPK :: Eq (PK a) => a -> a -> Bool 
equalPK = (==) `on` getPK 

instance Eq MyType1 where (==) = equalPK 
... 
+0

Vâng, sau khi xác định trường hợp đó, tôi đã nhận 'src/Utils.hs: 52: 20: Chồng chéo trường hợp cho số nguyên Eq phát sinh từ việc sử dụng '/ ='… Các trường hợp phù hợp: Ví dụ Eq Số nguyên - Được xác định trong trường hợp ‘số nguyên-gmp: GHC.Integer.Type’ (Eq b, HasPK a b) => Eq a'. Nhưng tôi đã không mong đợi lỗi đó vì 'Integer' không có một phiên bản' HasPk Integer b'. Tôi muốn hiểu lỗi nếu tôi xác định cả hai Eq và HasPK, nhưng vì không có 'HasPK Integer', nên nó không trực tiếp sử dụng cá thể' Eq' bình thường? – utdemir

+2

@utdemir Vấn đề là: một 'instance C a => Eq a' áp dụng cho mọi kiểu, ngay cả với kiểu' C a' là false (!). Haskell sẽ cam kết sử dụng trường hợp này, và khi 'C a' nó sai nó sẽ lỗi, thay vì backtracking và tìm các trường hợp khác. Điều này được thực hiện bởi vì với backtracking vấn đề trở nên khó khăn hơn nhiều, và các nhà thiết kế Haskell đã quan tâm đến thời gian biên dịch.GHC có một phần mở rộng 'OverlappingInstances' để thư giãn hạn chế này, nhưng tôi không khuyến khích nó. Các trường hợp chồng chéo là tốt nhất để tránh, IMHO. – chi

+0

Cảm ơn bạn, tôi đã đi với giải pháp gia đình loại vì nó trông giống như phần mở rộng gây tranh cãi ít nhất là một. – utdemir

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