2011-12-17 39 views
20

Tôi đang cố gắng trải qua ví dụ YesNo từ Learn You a Haskell for Great Good! cuốn sách.Biến kiểu mơ hồ `a0 'trong các ràng buộc

Đây là mã nguồn của tôi:

module Main where 

main :: IO() 

main = putStrLn (show (yesno 12)) 

class YesNo a where 
    yesno :: a -> Bool 


instance YesNo Bool where 
    yesno b = b 

instance YesNo [a] where 
    yesno [] = False 
    yesno _ = True 


instance YesNo Int where 
    yesno 0 = False 
    yesno _ = True 

Khi tôi thực thi mã này ngoại lệ sau đây xảy ra:

Ambiguous type variable `a0' in the constraints: 
    (YesNo a0) arising from a use of `yesno' 
      at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:25-29 
    (Num a0) arising from the literal `12' 
      at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:31-32 
Probable fix: add a type signature that fixes these type variable(s) 
In the first argument of `show', namely `(yesno 12)' 
In the first argument of `putStrLn', namely `(show (yesno 12))' 
In the expression: putStrLn (show (yesno 12)) 

bạn có thể vui lòng giải thích những gì xảy ra với mã này?

Trả lời

31

Vấn đề là nó không biết loại 12 là gì! Nó có thể là bất kỳ loại với một trường hợp Num:

Bạn cần phải xác định loại bạn muốn trực tiếp: thử putStrLn (show (yesno (12 :: Int))).

Tại sao GHC không thể chọn Int, vì không có lựa chọn nào khác có hiệu quả, bạn hỏi? Câu hỏi hay. Câu trả lời là với hệ thống kiểu chữ của Haskell, việc thêm một cá thể không bao giờ có thể làm mất hiệu lực các chương trình chính xác hiện có hoặc thay đổi hành vi của chúng. (Điều này được gọi là giả định thế giới mở mở.) Nếu nó đã chọn Int, thì điều gì sẽ xảy ra nếu bạn thêm instance YesNo Integer? Sự lựa chọn sẽ trở nên mơ hồ, và chương trình của bạn sẽ phá vỡ!

Vì vậy, khi bạn muốn sử dụng một kiểu chữ như thế này với một giá trị đa hình, bạn phải xác định loại bạn có nghĩa là chính xác hơn. Điều này không nên xuất hiện nhiều trong thực tế, vì thường sẽ có một số bối cảnh xung quanh để ép buộc loại đó là những gì bạn muốn; nó chủ yếu là chữ số bằng chữ số bị ảnh hưởng bởi điều này.

5

Vấn đề là 12 thực sự có loại Num a => a và không Int như bạn mong đợi. Nếu bạn thêm một loại an toàn rõ ràng như 12 :: Int, nó sẽ biên dịch.

+0

Có thể tạo ra loại của riêng bạn của nhà xây dựng dữ liệu mà làm việc như '12' trong Haskell? Dường như bất cứ khi nào bạn tạo một hàm tạo dữ liệu, nó xây dựng một giá trị của một kiểu duy nhất. Nhưng khi bạn viết '12', bạn có thể thấy nó không phải là giá trị của một kiểu đơn lẻ, mà là một giá trị của bất kỳ kiểu nào mà kiểu đó bị ràng buộc bởi Num. Do đó 'Num a => a'. Tôi có giải thích điều này một cách chính xác không? Loại hình này có giống như xây dựng một giá trị có tồn tại/công đoàn không? – CMCDragonkai

+0

@CMCDragonkai Tôi hơi mờ về chi tiết nhưng về cơ bản chữ "Num' chứa một hàm' fromInteger :: Num a => Integer -> a'. Một chữ số làm việc như thể 'fromInteger' được gọi trên đó. – fuz

+0

Ah, do đó, 'a' không trở thành một công đoàn tồn tại. Rất thú vị. – CMCDragonkai

1

Tôi gặp vấn đề tương tự.

Đây là một giải pháp, có lẽ không phải là tốt nhất nhưng nó hoạt động:

class YesNo a where 
    yesno :: a -> Bool 


instance YesNo Int where 
    yesno 0 = False 
    yesno _ = True 


instance YesNo Integer where 
    yesno 0 = False 
    yesno _ = True 
Các vấn đề liên quan