2012-11-05 32 views
8

Tôi có quyền kết luận rằng không có cách nào để tính maxBound - minBound trong Haskell cho một loại tùy ý EnumBounded? Hoặc tôi thiếu một số thủ thuật/hack? Đây là những gì tôi có, mà rõ ràng là không thể làm việc:Không thể tính toán sự khác biệt giữa maxBound và minBound của một kiểu Enum?

difference :: (Enum a, Bounded a) => Int 
difference = fromEnum maxBound - fromEnum minBound 

Lỗi:

Foo.hs:37:1: 
    Ambiguous constraint `Enum a' 
     At least one of the forall'd type variables mentioned by the constraint 
     must be reachable from the type after the '=>' 
    In the type signature for `difference': difference :: (Enum a, Bounded a) => Int 

Foo.hs:37:1: 
    Ambiguous constraint `Bounded a' 
     At least one of the forall'd type variables mentioned by the constraint 
     must be reachable from the type after the '=>' 
    In the type signature for `difference': difference :: (Enum a, Bounded a) => Int 

Tôi hiểu tại sao tôi nhận được rằng lỗi không có hạn thực tế trong đó có loại a, vì vậy nó không thể tìm ra những gì a là. Câu hỏi đặt ra là liệu có cách nào để giải quyết vấn đề này không.

+0

Tôi hỏi sai lần đầu tiên xung quanh. Tôi đã chỉnh sửa câu hỏi. –

+0

bạn sẽ sử dụng * giá trị như thế này như thế nào? – SingleNegationElimination

+0

@SingleNegationElimination Tôi có kiểu Enum/Bounded, và muốn xây dựng một cây của tất cả các giá trị 'fromEnum' có thể cho loại đó. Rất thuận tiện để có thể có một hàm đệ quy như 'mkTree lower upper' (tuy nhiên nó không hoạt động như thế), nó phá vỡ vấn đề thành các phạm vi ints nhỏ hơn. Nhưng để bắt đầu, bạn cần có khả năng viết 'mkTree (fromEnum minBound) (fromEnum maxBound)' và nhận được giới hạn của kiểu đúng. – amalloy

Trả lời

9

Sử dụng Proxy để chỉ định loại bạn muốn và sử dụng ScopedTypeVariables để đưa loại đó vào phạm vi trong định nghĩa chức năng của bạn.

{-# LANGUAGE ScopedTypeVariables #-} 

data Proxy a = Proxy 

difference :: forall a . (Enum a, Bounded a) => Proxy a -> Int 
difference Proxy = fromEnum (maxBound :: a) - fromEnum (minBound :: a) 

>>> difference (Proxy :: Proxy Bool) 
1 

Edit: Sử dụng gợi ý của Daniel:

data Proxy a = Proxy 

difference :: (Enum a, Bounded a) => Proxy a -> Int 
difference p = fromEnum (max' p) - fromEnum (min' p) where 
    max' :: (Bounded a) => Proxy a -> a 
    max' Proxy = maxBound 
    min' :: (Bounded a) => Proxy a -> a 
    min' Proxy = minBound 
+3

Điều này cũng có thể được thực hiện mà không có biến kiểu phạm vi, nếu bạn muốn tránh các phần mở rộng. 'foo :: Bounded a => Proxy a -> (a, a); foo _ = (minBound, maxBound); sự khác biệt p = let (min, max) = foo p trong từEnum max - fromEnum min' –

+0

@DanielWagner Cảm ơn. Tôi thích phiên bản của bạn tốt hơn. –

+2

Tôi phải nói rằng tôi thích 'Proxy' với tham số kiểu phantom tốt hơn câu trả lời của Dave sử dụng' undefined'. –

12
difference :: (Enum a, Bounded a) => a -> Int 
difference x = fromEnum (maxBound `asTypeOf` x) 
      - fromEnum (minBound `asTypeOf` x) 

Gọi nó như ví dụ difference (undefined :: Char).

Nhưng lưu ý rằng điều này sẽ tràn cho một số loại (đáng chú ý nhất Int), do đó thay vì sử dụng một Integer kết quả:

difference :: (Enum a, Bounded a) => a -> Integer 
difference x = toEnum (fromEnum (maxBound `asTypeOf` x)) 
      - toEnum (fromEnum (minBound `asTypeOf` x)) 
Các vấn đề liên quan