2014-11-09 32 views
7

xem xét như sau:Hạn chế suy luận từ Instances

{-# LANGUAGE FlexibleContexts #-} 
module Foo where 

data D a = D a 
class Foo b 

instance (Num a) => Foo (D a) 

f :: (Foo (D a)) => a -> a 
f x = x+1 

GHC phàn nàn rằng nó không thể suy ra Num a trong f. Tôi muốn hạn chế này được suy ra từ trường hợp (không chồng chéo) của Foo cho D a.

Tôi biết tôi có thể sử dụng một GADT cho D và thêm các hạn chế Num a ở đó, nhưng tôi hy vọng để không phải làm ô nhiễm các nhà xây dựng cho D với rất nhiều trở ngại không cần thiết. Có bất kỳ hy vọng nào về điều này xảy ra không, và liệu có thể bây giờ?

Trả lời

5

Mũi tên =>directional. Điều đó có nghĩa là nếuNum a giữ sau đó Foo (D a). Nó không có nghĩa là nếu giữ Foo (D a) thì giữ Num a.

Kiến thức có (và sẽ không bao giờ) bất kỳ trường hợp trùng lặp nào cho Foo (D a) nên ngụ ý rằng hàm ý ngược lại cũng đúng, nhưng (a) GHC không biết điều này và (b) máy móc cá thể của GHC không được đặt sử dụng kiến ​​thức này.

Để thực sự biên dịch các chức năng sử dụng các lớp kiểu, nó không đủ để GHC chỉ chứng minh rằng một kiểu phải là một thể hiện của một lớp. Nó phải thực sự đưa ra một tuyên bố cá thể cụ thể cụ thể cung cấp các định nghĩa về các hàm thành viên. Chúng ta cần một bằng chứng mang tính xây dựng, không chỉ là bằng chứng tồn tại.

Để xác định một thể hiện của lớp C, nó có thể sử dụng lại một đối tượng được chọn bởi người gọi của hàm đang được biên soạn hoặc phải biết các loại có đủ cụ thể để chọn một cá thể đơn lẻ. Hàm được biên dịch sẽ chỉ được truyền một cá thể cho C nếu nó có một ràng buộc cho C; nếu không thì hàm phải đủ đơn hình mà nó chỉ có thể sử dụng một cá thể đơn lẻ.

Xem xét ví dụ của bạn cụ thể, chúng ta có thể thấy rằng f có một ràng buộc cho Foo (D a), vì vậy chúng tôi có thể dựa vào người gọi cung cấp điều đó cho chúng tôi. Nhưng người gọi sẽ không cung cấp cho chúng tôi một ví dụ cho Num a. Thậm chí nếu bạn cho rằng chúng ta đã biết từ Num a hạn chế về Foo (D a) rằng phải có trường hợp như vậy đâu đó ngoài kia, chúng tôi không có ý tưởng gì a là, vì vậy mà định nghĩa của + chúng ta nên gọi? Chúng tôi thậm chí không thể gọi khác chức năng mà làm việc cho bất kỳ Num a nhưng được định nghĩa bên ngoài lớp học, bởi vì tất cả họ sẽ có Num a hạn chế và do đó mong đợi chúng tôi để xác định một thể hiện cho họ. Biết rằng có một thể hiện mà không cần phải trường hợp chỉ là không hữu ích.

Nó không phải là tất cả rõ ràng, nhưng những gì bạn đang thực sự yêu cầu GHC làm là để làm một chuyển đổi thời gian chạy trên loại a mà đến lúc chạy. Điều này là không thể, bởi vì chúng ta phải phát ra mã hoạt động cho bất kỳ loại nào trong Num, thậm chí các kiểu chưa tồn tại hoặc các cá thể của chúng chưa tồn tại.

Một ý tưởng tương tự mà không công việc là khi bạn có một hạn chế trên lớp hơn là trên dụ. Ví dụ:

class Num a => Foo a 

f :: Foo a => a -> a 
f x = x + 1 

Nhưng chỉ tác phẩm này vì chúng ta biết rằng tất cả Foo trường hợp phải có một Num dụ tương ứng, và do đó tất cả người gọi của một hàm đa hình trong Foo a biết cũng để chọn một trường hợp Num. Vì vậy, ngay cả khi không biết cái đặc thù a để chọn một trường hợp Num, f biết rằng người gọi của nó cũng sẽ cung cấp một ví dụ Num cùng với Foo dụ.

Trong trường hợp của bạn, lớpFoo không biết gì về Num. Trong các ví dụ khác Num thậm chí có thể không được xác định trong mã có thể truy cập vào mô-đun mà lớp học Foo được xác định. Đó là lớp mà bộ các thông tin cần thiết mà đã được cung cấp để gọi một chức năng đó là đa hình trong lớp loại, và những chức năng đa hình có để có thể làm việc mà không bất kỳ kiến ​​thức cụ thể cho một ví dụ cụ thể.

Vì vậy, các Num a => Foo (D a) dụ không thể cửa hàng Num dụ - thực sự, định nghĩa ví dụ cũng là đa hình trong a, vì vậy nó không thể chọn một trường hợp đặc biệt để lưu trữ thậm chí nếu có không gian! Vì vậy, mặc dù f có thể biết rằng có một ví dụ Num a từ Foo (D a) (nếu chúng tôi đoán chắc chắn rằng không có sự chồng chéo nào có thể được tham gia), nó vẫn cần một ràng buộc Num a để yêu cầu người gọi của mình chọn Num ví dụ để nó sử dụng.

+0

"Nó không phải là tất cả rõ ràng, nhưng những gì bạn đang thực sự yêu cầu GHC để làm là để làm một chuyển đổi thời gian chạy trên loại' a' đến lúc chạy. " Tôi sắp phản đối rằng, các thủ thuật GADT vắng mặt, khi chạy mọi thứ cần phải được đơn hình và do đó giá trị của biến kiểu 'a' phải được biết tại thời gian biên dịch, nhưng [không thực sự là như vậy] (http: // stackoverflow .com/a/10535629/1186208). Tuy nhiên, tôi không hiểu tại sao khía cạnh thời gian chạy lại quan trọng ở đây. –

+1

@ChristianConkle Xuất hiện với một thể hiện 'Num' * bên trong * một hàm (không khai báo trong giao diện thông qua một ràng buộc) sẽ yêu cầu biết kiểu để bạn có thể tìm thấy cá thể đó. Vì 'f' là đa hình, nó có thể được gọi trên các kiểu khác nhau ở các thời điểm khác nhau, vì vậy công tắc trên kiểu sẽ phải được thực hiện khi chạy (điều này là không thể, vì các kiểu không tồn tại ** ở tất cả ** khi chạy , hãy để một mình một loại bản đồ từ điển hoàn chỉnh cho các trường hợp Num). Câu trả lời đó có đáp ứng được câu hỏi của bạn không? – Ben

7

Tôi đoán điều này sẽ phá vỡ các trường hợp trùng lặp và do đó không được phỏng đoán nói chung. Nghĩa là, bạn có thể có

{-# LANGUAGE OverlappingInstances #-} 
... 
instance (Num a) => Foo (D a) 

instance Foo (D Bool) 

và sau đó suy luận mong muốn của bạn chắc chắn sẽ không được âm thanh.

EDIT: Nghiên cứu kỹ hơn the documentation, nó có thể có

{-# LANGUAGE FlexibleContexts #-} 
module Foo where 

data D a = D a 
class Foo b 

instance (Num a) => Foo (D a) 

f :: (Foo (D a)) => a -> a 
f x = x+1 

và sau đó trong một tập tin riêng:

{-# LANGUAGE OverlappingInstances #-} 
module Bar where 

import Foo 

instance Foo Bool 

test = f True 

Đó là, các tài liệu hướng dẫn chỉ ngụ ý một của các mô-đun xác định hai trường hợp cần phải có cờ OverlappingInstances, vì vậy nếu Foo.f có thể xác định được như vậy, bạn có thể làm khác module Bar an toàn loại phá vỡ hoàn toàn. Lưu ý rằng với trình biên dịch riêng biệt của GHC, f sẽ được biên dịch hoàn toàn mà không cần biết về mô-đun Bar.

+0

Tôi đồng ý này sẽ khó khăn hơn với 'OverlappingInstances', nhưng nó phải là có thể mà không có nó tôi nghĩ. Ngay cả với các trường hợp trùng lặp, nếu một ràng buộc là phổ biến giữa tất cả các trường hợp, nó có thể được suy ra. – crockeea

+5

@Eric Vấn đề, như thể hiện trong ví dụ đã chỉnh sửa của tôi, là việc biên dịch riêng biệt của GHC + giả định thế giới mở cho các lớp học ngăn cản nguyên tắc hầu như bất kỳ kiến ​​thức nào về "tất cả các trường hợp" của một lớp. –

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