2011-12-16 61 views
11

Chơi xung quanh với kiểu lớp tôi đã đưa ra dường như vô tộiTypeclass dụ với phụ thuộc chức năng không hoạt động

class Pair p a | p -> a where 
    one :: p -> a 
    two :: p -> a 

Điều này dường như làm việc tốt, ví dụ

instance Pair [a] a where 
    one [x,_] = x 
    two [_,y] = y 

Tuy nhiên, tôi gặp sự cố với bộ dữ liệu. Mặc dù định nghĩa sau biên dịch ...

instance Pair (a,a) a where 
    one p = fst p 
    two p = snd p 

... Tôi không thể sử dụng nó như tôi mong đợi:

main = print $ two (3, 4) 

No instance for (Pair (t, t1) a) 
    arising from a use of `two' at src\Main.hs:593:15-23 
Possible fix: add an instance declaration for (Pair (t, t1) a) 
In the second argument of `($)', namely `two (3, 4)' 
In the expression: print $ two (3, 4) 
In the definition of `main': main = print $ two (3, 4) 

Có cách nào để xác định các trường hợp chính xác? Hay tôi phải sử dụng trình bao bọc newtype?

Trả lời

18

Ví dụ của bạn chỉ hoạt động tốt. Quan sát:

main = print $ two (3 :: Int, 4 :: Int) 

Điều này hoạt động như mong đợi. Vậy tại sao nó không hoạt động nếu không có chú giải kiểu, vậy thì sao? Vâng, hãy xem xét loại của tuple: (3, 4) :: (Num t, Num t1) => (t, t1). Vì các chữ số là đa hình, không có gì yêu cầu chúng là cùng loại. Ví dụ được định nghĩa cho (a, a), nhưng sự tồn tại của cá thể đó sẽ không nói cho GHC hợp nhất các loại (vì nhiều lý do chính đáng). Trừ khi GHC có thể suy ra bằng các phương tiện khác mà hai loại này giống nhau, nó sẽ không chọn trường hợp bạn muốn, ngay cả khi hai loại có thể được thực hiện như nhau.

Để giải quyết vấn đề của bạn, bạn chỉ có thể thêm chú thích loại, như tôi đã làm ở trên. Nếu các đối số đến từ những nơi khác, nó thường không cần thiết vì chúng đã được biết là cùng loại, nhưng nó sẽ vụng về một cách nhanh chóng nếu bạn muốn sử dụng các chữ số.

Một giải pháp thay thế là cần lưu ý rằng, vì cách hoạt động của cá thể hoạt động, có một cá thể cho (a, a) có nghĩa là bạn không thể viết một thể hiện như (a, b) cũng như nếu bạn muốn. Vì vậy, chúng ta có thể lừa dối một chút, để buộc sự thống nhất bằng cách sử dụng lớp loại, như thế này:

instance (a ~ b) => Pair (a,b) a where 

Điều đó cần mở rộng TypeFamilies cho ~ bối cảnh, tôi nghĩ. Điều này có nghĩa là cho phép cá thể đối sánh với bất kỳ tuple nào lúc đầu, vì lựa chọn cá thể bỏ qua ngữ cảnh. Sau khi chọn cá thể, tuy nhiên, ngữ cảnh a ~ b khẳng định sự bình đẳng loại, sẽ tạo ra lỗi nếu chúng khác nhau nhưng - quan trọng hơn ở đây - sẽ thống nhất các biến kiểu nếu có thể. Sử dụng điều này, định nghĩa của bạn về main hoạt động như bình thường, không có chú thích.

+0

Cảm ơn, rất thú vị! – Landei

6

Vấn đề là một chữ số có loại đa hình. Nó không phải là rõ ràng cho typechecker rằng cả hai literals nên có cùng một loại (Int). Nếu bạn sử dụng một cái gì đó không phải là đa hình cho các bộ dữ liệu của bạn, mã của bạn sẽ hoạt động. Hãy xem xét các ví dụ sau:

*Main> two (3,4) 

<interactive>:1:1: 
    No instance for (Pair (t0, t1) a0) 
     arising from a use of `two' 
    Possible fix: add an instance declaration for (Pair (t0, t1) a0) 
    In the expression: two (3, 4) 
    In an equation for `it': it = two (3, 4) 
*Main> let f = id :: Int -> Int -- Force a monomorphic type 
*Main> two (f 3,f 4) 
4 
*Main> two ('a','b') 
'b' 
*Main> two ("foo","bar") 
"bar" 
*Main> two (('a':),('b':)) "cde" 
"bcde" 
*Main> 
Các vấn đề liên quan