2017-11-08 27 views
9

xem xét mã này:Thêm một trường hợp không sử dụng sửa chữa một lỗi loại

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

module Foo where 

class Foo a 

class SomeClass a 
instance {-# OVERLAPPABLE #-} (Foo a) => SomeClass a 

bar :: (SomeClass a) => a -> Int 
bar = const 0 

foo :: (SomeClass a) => a -> Int 
foo t = let x = bar t in x 

Ở đây, foo cuộc gọi bar, và sẽ có thể làm như vậy bằng cách sử dụng SomeClass hạn chế trong bối cảnh của nó. Thay vào đó, GHC giả định nó phải làm như vậy bằng cách sử dụng Foo a => SomeClass a dụ:

Foo.hs:16:17: 
    Could not deduce (Foo a) arising from a use of ‘bar’ 
    from the context (SomeClass a) 
     bound by the type signature for foo :: SomeClass a => a -> Int 
     at Foo.hs:15:8-32 
    Possible fix: 
     add (Foo a) to the context of 
     the inferred type of x :: Int 
     or the type signature for foo :: SomeClass a => a -> Int 
    In the expression: bar t 
    In an equation for ‘x’: x = bar t 
    In the expression: let x = bar t in x 

Có hai cách để sửa lỗi này:

  1. Trong foo, thay đổi let x = bar t in x để bar t
  2. Phụ thêm dòng instance SomeClass Int để tôi chương trình

Điều gì đang xảy ra ở đây? Tại sao sự cố này xảy ra và tại sao các bản sửa lỗi này hoạt động?


Ví dụ này, tất nhiên, được đơn giản hóa bởi vấn đề thực tế của tôi. Tôi đã gặp nó trong quá trình làm việc trên khung công tác Cubix để chuyển đổi đa ngôn ngữ (arxiv.org/pdf/1707.04600).

Vấn đề thực tế của tôi liên quan đến một hàm có ràng buộc InjF f IdentL FunctionExpL ("f là ngôn ngữ có thể sử dụng mã định danh để biểu thị hàm trong cuộc gọi hàm"). Tôi có một trường hợp (có thể chồng chéo) (FunctionIdent :<: f) => InjF f IdentL FunctionExpL, mà máy đánh chữ thu giữ, cho tôi một lỗi giả mạo Could not deduce FunctionIdent :<: f.

Lỗi này vẫn tồn tại ngay cả khi có một trường hợptrong phạm vi cho một Foo cụ thể, do đó, câu trả lời của leftaroundabout, dự đoán rằng trường hợp khác cần khắc phục sự cố, không phải là toàn bộ câu chuyện.

+2

'{- # OVERLAPPABLE # -}' và '{- # LANGUAGE FlexibleContexts # -}' không ảnh hưởng gì cả. Một cách khác để biên dịch: thêm một 'x :: Int' vào liên kết' let'. – Alec

+1

Có lẽ điều silliest là nó cũng typechecks khi có _no dụ tại all_ trong module ... – leftaroundabout

Trả lời

3

Lý do cho điều này là trình biên dịch cố gắng để làm cho x càng chung càng tốt: nó muốn nó là (SomeClass a) => Int (thông báo đây sẽ là một loại mơ hồ nếu bạn tự viết nó ra). Một cách để ngăn chặn kiểu cục bộ lạ này là kích hoạt -XMonoLocalBinds, nhưng tôi thực sự không khuyên bạn nên sử dụng nó.

Bây giờ, trình biên dịch tiếp tục đánh máy. Tại thời điểm đó, có chính xác một trong số instance SomeClass trong phạm vi, cụ thể là bắt tất cả (Foo a) => SomeClass a, vì vậy không có sự mơ hồ. (Về nguyên tắc, Haskell hoàn toàn cấm không rõ ràng về độ phân giải ví dụ; OVERLAPPABLE chuyển đổi điều này nhưng nó chỉ nhảy vào hành động khi thực sự có nhu cầu, như khi bạn có thêm instance SomeClass Int.) Do đó, trình biên dịch ngay lập tức cam kết với trường hợp đó và do đó loại bỏ cho kiểm tra loại c. Rằng nó thực hiện điều này là cần thiết cho các tình huống mà bạn thực sự muốn một ràng buộc lớp được thay thế bằng ràng buộc của một cá thể. Điều này có vẻ vô dụng trong ví dụ được đưa ra, nhưng nó thực sự quan trọng khi bạn có thể hiện của các hình thức

instance Bar a => SomeClass (Maybe a) 

... mà là nhiều hơn nữa hợp lý hơn so với mã của bạn vì đó không có thể được diễn tả như một lớp cha ràng buộc, và hoàn toàn ok mà không có bất kỳ sự chồng chéo nào. Nếu trình biên dịch không làm giảm ràng buộc giải quyết xuống đến Bar a trong những trường hợp đó, nó có thể không bao giờ thực sự giải quyết xuống loại Maybe.

Kết hợp: tránh các trường hợp trùng lặp; thể hiện mối quan hệ lớp học của các khai báo siêu lớp nếu có thể, nếu không, hãy sửa lại các ràng buộc thành GADT (điều này là khó xử nhưng cho phép bạn kiểm soát chính xác nơi mà ràng buộc sẽ được sử dụng).

+0

1) Bạn có ý nghĩa gì (Foo a => SomeClass a) là trường hợp duy nhất trong phạm vi? Điều gì về ràng buộc (SomeClass a) trong tuyên bố? –

+0

2) Tại sao cần phải đơn giản hóa ràng buộc SomeClass (Có thể là) ngay lập tức để đơn giản hóa nó? Tại sao không thể trì hoãn bước phân giải đó cho đến cuối sau khi nó đã xem xét ngữ cảnh khai báo? –

+0

3) Điều này giúp giải thích ví dụ đơn giản mà tôi đã đưa ra, nhưng nó không phù hợp với ví dụ thực tế. Tôi sẽ sửa bài viết của mình để đưa ra ví dụ thực sự. –

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