Mũi tên =>
là 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ó là một thể hiện mà không cần phải có 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ó là 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.
"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. –
@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