2014-11-12 17 views
11

Tôi hiểu rằng khi cóTại sao ngữ cảnh không được xem xét khi chọn trường hợp typeclass trong Haskell?

instance (Foo a) => Bar a 
instance (Xyy a) => Bar a 

GHC không xem xét bối cảnh, và các trường hợp được báo cáo như trùng lặp.

Điều gì là phản trực giác, mà (tôi đoán) sau khi chọn một cá thể, nó vẫn cần phải kiểm tra xem ngữ cảnh có phù hợp hay không, và nếu không, loại bỏ cá thể. Vì vậy, tại sao không đảo ngược thứ tự, và loại bỏ các trường hợp với bối cảnh không phù hợp, và tiến hành với các thiết lập còn lại.

Điều này có thể bị cản trở theo một cách nào đó không? Tôi thấy làm thế nào nó có thể gây ra độ phân giải hạn chế hơn làm việc trả trước, nhưng cũng giống như có UndecidableInstances/IncoherentInstances, không thể có một ConsiderInstanceContexts khi "Tôi biết những gì tôi đang làm"?

+1

Ví dụ GHC nên chọn nếu 'a' là' Foo' và 'Xyy'? – mb14

+1

@ mb14: Tùy ý. ('IncoherentInstances' đã làm một cái gì đó tương tự, và tôi có thể sống với điều đó). – ron

+0

Thật vậy, sự khác biệt duy nhất có vẻ là 'IncoherentInstances' cho phép GHC cam kết với một trong hai trường hợp, có thể loại bỏ một trường hợp có ngữ cảnh thỏa mãn và cam kết với một trường hợp không thỏa mãn (sẽ kích hoạt lỗi). Câu hỏi này thay vì hỏi tại sao GHC không có cờ 'BacktrackOnContextFailures', nếu tôi hiểu chính xác, để cho trường hợp chính xác cuối cùng được thử. Nó chắc chắn sẽ dẫn đến sự hấp dẫn trong những trường hợp xấu nhất, nhưng chúng ta đã có 'UndecidableInstances' có thể tác động đáng kể đến hiệu suất. – chi

Trả lời

3

Điều này không trả lời bạn câu hỏi là tại sao lại xảy ra trường hợp này. Lưu ý, tuy nhiên, bạn luôn có thể định nghĩa một wrapper Newtype để disambiguate giữa hai trường hợp:

newtype FooWrapper a = FooWrapper a 
newtype XyyWrapper a = XyyWrapper a 

instance (Foo a) => Bar (FooWrapper a) 
instance (Xyy a) => Bar (XyyWrapper a) 

này có lợi thế thêm rằng bằng cách thông qua xung quanh, hoặc một FooWrapper hoặc một XyyWrapper bạn kiểm soát một cách rõ ràng nào trong hai trường hợp bạn muốn sử dụng nếu bạn xảy ra thỏa mãn cả hai.

+0

Chắc chắn. Trường hợp sử dụng của tôi sẽ giống như "chọn một trường hợp tốt nhất", mà không có khách hàng phải bao bọc/biết bất cứ điều gì. Ví dụ: có một lớp 'Tìm kiếm tuyến tính',' Tìm kiếm nhị phân' và 'Tìm kiếm', trong đó' Tìm kiếm' mặc định tìm kiếm nhị phân nếu cá thể tồn tại, tìm kiếm tuyến tính nếu không (nếu có). (Vâng, sự lựa chọn tùy ý sẽ không đủ tốt ở đây, giống như một sự lựa chọn theo thứ tự, nhưng hãy bỏ qua một bên). – ron

+0

Để giải trí, chúng ta không xem xét việc thay thế các trường hợp rõ ràng của 'Tìm kiếm' cho mỗi kiểu dữ liệu. – ron

+4

Tôi thấy rằng đây là một thử nghiệm suy nghĩ và một câu hỏi thú vị (mà tôi không thể trả lời). Tuy nhiên, FWIW, tôi sẽ xem xét nó là một thiết kế xấu cho trình biên dịch để làm cho các lựa chọn tùy ý hoặc thậm chí không xác định mà hoàn toàn thay đổi hành vi của mã của bạn. – DanielM

2

Các lớp học hơi lạ. Ý tưởng ban đầu (mà vẫn còn khá nhiều công trình) là một loại đường cú pháp xung quanh những gì nếu không sẽ là data báo cáo. Ví dụ: bạn có thể tưởng tượng:

data Num a = Num {plus :: a -> a -> a, ... , fromInt :: Integer -> a} 
numInteger :: Num Integer 
numInteger = Num (+) ... id 

thì bạn có thể viết các chức năng, ví dụ: loại:

test :: Num x -> x -> x -> x -> x 
test lib a b c = a + b * (abs (c + b)) 
    where (+) = plus lib 
      (*) = times lib 
      abs = absoluteValue lib 

Vì vậy, ý tưởng là "chúng tôi sẽ tự động lấy được tất cả mã thư viện này". Câu hỏi đặt ra là, làm sao chúng ta tìm được thư viện mà chúng ta muốn? Thật dễ dàng nếu chúng ta có một thư viện kiểu Num Int, nhưng làm thế nào để chúng tôi mở rộng nó thành "trường hợp bị hạn chế" dựa trên chức năng của loại:

fooLib :: Foo x -> Bar x 
xyyLib :: Xyy x -> Bar x 

Các giải pháp hiện tại trong Haskell là để làm một loại-Khuôn mẫu khớp với các kiểu đầu ra của các hàm đó và truyền các đầu vào cho khai báo kết quả. Nhưng khi có hai kết quả đầu ra của cùng loại, chúng ta sẽ cần một combinator mà kết hợp những thành:

eitherLib :: Either (Foo x) (Xyy x) -> Bar x 

và về cơ bản vấn đề là không có tốt chế-combinator của loại hình này ngay bây giờ. Đó là phản đối của bạn.

Vâng, đó là sự thật, nhưng có nhiều cách để đạt được điều gì đó về mặt đạo đức tương tự trong thực tế. Giả sử chúng ta định nghĩa một số chức năng với các loại:

data F 
data X 
foobar'lib :: Foo x -> Bar' x F 
xyybar'lib :: Xyy x -> Bar' x X 
bar'barlib :: Bar' x y -> Bar x 

y là một loại "loại ma" luồn qua tất cả những điều này, nhưng nó vẫn còn mạnh mẽ vì cho rằng chúng tôi muốn có một Bar x chúng tôi sẽ tuyên truyền sự cần thiết của một Bar' x y và cần có số điện thoại Bar' x y, chúng tôi sẽ tạo ra Bar' x X hoặc Bar' x y. Vì vậy, với các loại phantom và các lớp kiểu đa tham số, chúng ta có được kết quả mà chúng ta muốn.

Thông tin khác: https://www.haskell.org/haskellwiki/GHC/AdvancedOverlap

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