2012-12-30 18 views
5
toFloat :: (Floating a) => String -> a 
toFloat s = read s :: Float 

main = print (toFloat "1") 

Cung cấp cho tôi những lỗi:Haskell Typeclasses (Float không bao hàm sự nổi?)

Could not deduce (a ~ Float) 
from the context (Floating a) 

Tôi chắc rằng tôi đang thiếu một cái gì đó cơ bản, nhưng nó có vẻ như toFloat tôi nên luôn luôn trả về một phao và rằng Float nên ngụ ý nổi.

Trả lời

18

Chữ ký loại hứa hẹn kết quả sẽ là bất kỳ trường hợp nào của người gọi Floating mà người gọi muốn. Việc thực hiện nói "Biết những gì? Nevermind trên lời hứa rằng nó có thể là bất kỳ loại - chúng ta hãy chỉ cần làm cho nó một Float".

Sau đó trình biên dịch xuất hiện và nói "Whoa! Bạn không trả lại kiểu bạn đã hứa." Ngoại trừ việc nó đã thực sự cố gắng để làm cho chữ ký kiểu của bạn và việc thực hiện của bạn phù hợp. Nó nói với chính nó "Nếu điều này đã bị ràng buộc bằng cách nào đó, như vậy mà a luôn luôn là điều tương tự như Float, điều này sẽ là chính xác." Nó thực sự muốn tìm một cách mã của bạn là chính xác. Vâng, cách viết một ràng buộc như vậy là sử dụng ~, toán tử bình đẳng kiểu. Một ràng buộc của (a ~ Float) có nghĩa là "a là cùng loại với Float". Vì vậy trình biên dịch kiểm tra ngữ cảnh mà bạn đã cung cấp trong chữ ký kiểu, và nó không tìm thấy ràng buộc đó. Và nó chạy ra khỏi các cách để làm cho chữ ký và thực hiện của bạn làm việc cùng nhau, từ bỏ và báo cáo lỗi.

Thật không may, lỗi báo cáo hơi mờ đục do nỗ lực đưa vào mã của bạn hoạt động như thế nào. Chỉ là sự thay đổi nhỏ nhất, thêm một chút hạn chế, sẽ làm cho nó đúng. Vì vậy, nó báo cáo rằng hạn chế đó không có mặt. Nhưng nó không báo cáo lý do tại sao nó đang tìm kiếm sự hạn chế đó, làm cho toàn bộ mọi thứ không rõ ràng nếu bạn chưa từng thấy nó trước đây.

+0

Cảm ơn phản ứng nhanh. Điều này làm rõ vấn đề cho tôi một cách hoàn hảo. Bằng cách nào đó nó đã không bấm cho tôi rằng người gọi chức năng của tôi có thể yêu cầu một trường hợp cụ thể của typeclass của tôi. – nottombrown

5

Bạn đang nói toFloat có thể trả về bất kỳ loại nào thuộc về Floating typeclass nhưng bạn đang hạn chế nó thành Float, điều đó là sai. Chức năng của bạn là đa hình trong a vì vậy bạn không thể trả về một phiên bản Floating, nó sẽ có thể hoạt động với tất cả các phiên bản.

Otherway bạn có thể hiểu được điều này bằng cách

toFloat :: (Read a,Floating a) => String -> a 
toFloat s = read s 

Trong ghci

*Main> :t toFloat "12.1" 
toFloat "12.1" :: (Floating a, Read a) => a 
*Main> :t (toFloat "12.1" :: Float) 
(toFloat "12.1" :: Float) :: Float 
*Main> :t (toFloat "12.1" :: Double) 
(toFloat "12.1" :: Double) :: Double 

Kể từ khi nó trả về kiểu thuộc typeclass Floating bạn sẽ có thể chuyển nó sang bất kỳ loại (thuộc Floating) bởi cung cấp chữ ký loại rõ ràng sau khi hàm được áp dụng. Mặt khác, hãy nhớ trường hợp của bạn khi bạn đang quay trở lại Float bây giờ bạn không thể chỉ nói rằng tôi mong đợi Double từ chức năng này vì điều đó không thể xảy ra mà không có chuyển đổi rõ ràng.

Một cách khác để hiểu làm thế nào khủng khiếp giả định của bạn là xem xét chức năng read

read :: Read a => String -> a 

Bây giờ ở đây theo bạn, bạn chỉ có thể trở lại nói Int cho tất cả mọi thứ vì Int có một thể hiện cho Read. Giờ đây, bạn có thể hiểu điều gì sẽ xảy ra nếu bạn làm điều gì đó như

read "12" + (1.2 :: Double) 
1

này cũng đơn giản như bạn có thể có nó:

-- The simplest.                                    
toFloat :: String -> Float 
toFloat = read 

-- More generalized.                                   
toFloat' :: (Floating a, Read a) => String -> a 
toFloat' = read 
8

Câu hỏi này hoặc một cái gì đó rất giống với nó được hỏi một cách thường xuyên. (Đó không phải là đơn khiếu nại, tôi chỉ đang chỉ ra rằng bạn không phải là người duy nhất bị nhầm lẫn bởi điều này.)

Trong ngôn ngữ OO, bạn có thể nói "hàm này trả về thứ gì đó triển khai X". Sau đó, hàm có thể trả về bất kỳ địa ngục nào mà bạn cảm thấy muốn quay trở lại, miễn là thực tế là thực hiện X.

Haskell không hoạt động như vậy. Nếu bạn nói "hàm này trả về một cái gì đó thực hiện X", thì hàm phải có khả năng mang lại bất kỳ loại có thể nào triển khai X!

Sự khác biệt chính là: Trong ngôn ngữ OO, chức năng quyết định loại trả về (trong các ràng buộc được chỉ định). Trong Haskell, người gọi quyết định loại trả lại (một lần nữa, trong các ràng buộc quy định).

Một khi bạn hiểu sự khác biệt chính này, phần còn lại là khá hiển nhiên.

Một lần nữa, rất nhiều người dường như hiểu nhầm phần này. Có lẽ chúng ta nên đề cập đến nó nhiều hơn trong hướng dẫn và các công cụ, bởi vì nó có vẻ là một VFAQ ...

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