2013-08-26 55 views
9

Tôi đang phát triển một thư viện xử lý dữ liệu số chuyên ngành và tôi đã gặp một lỗi mà tôi không thể tìm ra cách khắc phục. Tôi nghĩ sẽ dễ dàng hơn để hiển thị một ví dụ trước và sau đó giải thích vấn đề của tôi. Tôi cũng xin lỗi vì những cái tên kỳ lạ, tôi phải làm xáo trộn vì mục đích pháp lý.Giải quyết thể hiện không rõ ràng cho lớp loại multiparam

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances  #-} 

data MyError = MyError String deriving (Eq, Show) 

data MyList = MyList [Double] deriving (Eq, Show) 
data NamedList = NamedList String MyList deriving (Eq, Show) 

class MyNum a b ret where 
    myAdd  :: a -> b -> Either MyError ret 
    myLessThan :: a -> b -> Either MyError Bool 

instance MyNum MyList Double MyList where 
    myAdd (MyList xs) x = Right $ MyList $ map (+x) xs 
    myLessThan (MyList xs) x = Right $ all (< x) xs 

instance MyNum NamedList Double NamedList where 
    myAdd (NamedList n l) x = fmap (NamedList n) $ myAdd l x 
    myLessThan (NamedList n l) x = myLessThan l x 

Nếu tôi cố gắng biên dịch này, tôi nhận được lỗi

No instance for (MyNum MyList Double ret0) 
    arising from a use of `myLessThan' 
The type variable `ret0' is ambiguous 
Possible fix: add a type signature that fixes these type variable(s) 
Note: there is a potential instance available: 
    instance MyNum MyList Double MyList 
    -- Defined at testing_instances.hs:13:10 
Possible fix: 
    add an instance declaration for (MyNum MyList Double ret0) 
In the expression: myLessThan l x 
In an equation for `myLessThan': 
    myLessThan (NamedList n l) x = myLessThan l x 
In the instance declaration for `MyNum NamedList Double NamedList' 

Bởi vì trình biên dịch không thể tìm ra mà cụ thể thể hiện của MyNum để sử dụng cho MyList. Nó hoạt động cho myAdd vì kiểu trả về cho MyNum có thể dễ dàng bắt nguồn, nhưng nó không thể tìm ra cho myLessThan. Tôi muốn sử dụng kiểu chữ này để tôi có thể dễ dàng thêm xử lý lỗi chi tiết trong suốt và vì mã thực của tôi có giá trị tương đương là +, -, *, /, <, < =,> và> = và tôi muốn để tạo một phiên bản cho MyNum Double MyList MyList, MyNum MyList MyList MyList và các phiên bản tương tự cho NamedList. Trừ khi có một cách dễ dàng hơn để làm điều này, nó là để tôi có thể có các toán tử giao hoán đa hình.

Tuy nhiên, tôi không thể tìm ra chữ ký loại để thêm vào myLessThan cho phiên bản thứ hai để có thể biết được cá thể nào cần sử dụng. Tôi biết một giải pháp là chia các toán tử số học và so sánh thành hai loại lớp riêng biệt, nhưng tôi muốn tránh làm như vậy nếu có thể.

Trả lời

11

Bạn có thể sử dụng functional dependencies để chỉ định rằng "ret được xác định duy nhất bởi ab".

... 
{-# LANGUAGE FunctionalDependencies #-} 
... 
class MyNum a b ret | a b -> ret where 
... 

Điều này cho phép các typechecker biết rằng nó có thể chọn định nghĩa ví dụ đúng, chỉ biết ab từ các đối số ở của bạn:

myLessThan (NamedList n l) x = myLessThan l x 

Trình biên dịch bây giờ sẽ phàn nàn nếu bạn định nghĩa thêm ví dụ có cùng số ab nhưng khác ret, chẳng hạn như

instance MyNum MyList Double SomeOtherType where 
+0

Đây là một cấu trúc trước đây không quen thuộc trong Haskell đối với tôi và chính xác những gì tôi cần, cảm ơn! – bheklilr

+2

@bheklilr vui vì tôi có thể giúp. Bạn cũng có thể muốn tìm hiểu về TypeFamilies, dễ dàng hơn để làm việc với một số cách và mạnh mẽ hơn. – jberryman

4

Như jberryman đã lưu ý, bạn có thể sử dụng TypeFamilies. Và đây là cách:

-{-# LANGUAGE FlexibleInstances  #-} 
+{-# LANGUAGE TypeFamilies #-} 

-class MyNum a b ret where 
- myAdd  :: a -> b -> Either MyError ret 
+class MyNum a b where 
+ type Ret a b 
+ myAdd  :: a -> b -> Either MyError (Ret a b) 

-instance MyNum MyList Double MyList where 
+instance MyNum MyList Double where 
+ type Ret MyList Double = MyList 

-instance MyNum NamedList Double NamedList where 
+instance MyNum NamedList Double where 
+ type Ret NamedList Double = NamedList 

Tôi chỉ chuyển ret loại từ tham số lớp để associated typeRet.
Đây là TypeFamily -way cho biết rằng có chức năng từ tham số lớp ab đến Ret.

+0

Tôi thích nó loại bỏ tham số kiểu thứ ba rõ ràng khỏi lớp 'MyNum'. Có ai có bất kỳ thông tin mà trên đó một trong những hoạt động tốt hơn, mặc dù? Tôi không quan tâm nhiều hơn một chút lộn xộn mã nếu nó có nghĩa là nó chạy nhanh hơn, hoặc nếu loại gia đình có hiệu quả hơn, tôi muốn sử dụng giải pháp đó. – bheklilr

+1

Theo như tôi biết, cả hai giải pháp sẽ có hiệu suất như nhau.Bạn có thể kiểm tra lõi được biên dịch để xem có bất kỳ sự khác biệt nào không - nhưng trong trường hợp này, tôi sẽ không mong đợi có một. – ocharles

+0

@ocharles Tôi sẽ cố gắng lược tả khi tôi có cơ hội và tôi sẽ cố gắng đăng cập nhật sau với kết quả của mình – bheklilr