2010-04-30 27 views
8

Tôi muốn viết một phiên bản an toàn toEnum:an toàn và đa hình toEnum

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t 

Một thực hiện ngây thơ:

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t 
safeToEnum i = 
    if (i >= fromEnum (minBound :: t)) && (i <= fromEnum (maxBound :: t)) 
    then Just . toEnum $ i 
    else Nothing 

main = do 
    print $ (safeToEnum 1 :: Maybe Bool) 
    print $ (safeToEnum 2 :: Maybe Bool) 

Và nó không hoạt động:

safeToEnum.hs:3:21: 
    Could not deduce (Bounded t1) from the context() 
     arising from a use of `minBound' at safeToEnum.hs:3:21-28 
    Possible fix: 
     add (Bounded t1) to the context of an expression type signature 
    In the first argument of `fromEnum', namely `(minBound :: t)' 
    In the second argument of `(>=)', namely `fromEnum (minBound :: t)' 
    In the first argument of `(&&)', namely 
     `(i >= fromEnum (minBound :: t))' 

safeToEnum.hs:3:56: 
    Could not deduce (Bounded t1) from the context() 
     arising from a use of `maxBound' at safeToEnum.hs:3:56-63 
    Possible fix: 
     add (Bounded t1) to the context of an expression type signature 
    In the first argument of `fromEnum', namely `(maxBound :: t)' 
    In the second argument of `(<=)', namely `fromEnum (maxBound :: t)' 
    In the second argument of `(&&)', namely 
     `(i <= fromEnum (maxBound :: t))' 

Cũng như tôi hiểu được thông báo, trình biên dịch không nhận ra rằng minBoundmaxBound nên sản xuất chính xác cùng loại như trong loại kết quả của safeToEnum kiểm tra khai báo loại rõ ràng (:: t). Bất kỳ ý tưởng làm thế nào để sửa chữa nó?


Giải Quyết

Cả hai của camccann và giải pháp làm việc của Dave (mặc dù một Dave cần phải được điều chỉnh). Cảm ơn cả hai (nhưng tôi chỉ có thể chấp nhận một). Ví dụ làm việc với ScopedTypeVariables:

{-# LANGUAGE ScopedTypeVariables #-} 

safeToEnum :: forall t . (Enum t, Bounded t) => Int -> Maybe t 
safeToEnum i = 
    if (i >= fromEnum (minBound :: t)) && (i <= fromEnum (maxBound :: t)) 
    then Just . toEnum $ i 
    else Nothing 

Trả lời

13

Biến kiểu phạm vi là không cần thiết ở đây, bạn chỉ cần làm rõ cho GHC mà bạn mong đợi tất cả các công cụ Enum là cùng loại. Điều này rất dễ thực hiện bằng cách chuyển tất cả chúng đến một chức năng rõ ràng có nhiều loại Enum s cùng loại. Dưới đây là một cách:

enumIfBetween :: (Enum a) => a -> a -> Int -> Maybe a 
enumIfBetween a z x = let a' = fromEnum a 
          z' = fromEnum z 
         in if a' <= x && x <= z' 
         then Just $ toEnum x 
         else Nothing 

safeToEnum i = enumIfBetween minBound maxBound i 

main = do 
    print $ (safeToEnum 1 :: Maybe Bool) 
    print $ (safeToEnum 2 :: Maybe Bool) 

Đang cố gắng nó trong GHCi:

> main 
Just True 
Nothing 

Một giải pháp tổng quát hơn bằng cách sử dụng cùng một nguyên tắc là tiêu chuẩn thư viện chức năng asTypeOf, trong đó có hành vi tương tự như const nhưng đòi hỏi cả hai đối số cùng loại:

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t 
safeToEnum i = let r = toEnum i 
        max = maxBound `asTypeOf` r 
        min = minBound `asTypeOf` r 
       in if i >= fromEnum min && i <= fromEnum max 
       then Just r 
       else Nothing 

Phiên bản này cũng hoạt động.

Hãy nhớ rằng ScopedTypeVariables là phần mở rộng ngôn ngữ và do đó không nhất thiết phải di chuyển giữa các trình biên dịch. Trong thực tế hầu như tất cả mọi người sử dụng GHC, nhưng nó thường được ưa thích để dính vào ngôn ngữ cơ sở tiêu chuẩn (ví dụ, Haskell 98) khi có thể. Trong trường hợp này, ScopedTypeVariables thực sự là quá mức cần thiết; các Haskell wiki suggests asTypeOf as a portable replacement cho loại kịch bản.

+0

Có. Điều này cũng hoạt động. Ý kiến ​​hay. – sastanin

+0

Tôi thích các giải pháp của bạn. Thật tuyệt khi 'r' không thực sự được đánh giá trong' asTypeOf' (phiên bản thứ hai). – sastanin

+0

@jetxee: Vâng, nó chắc chắn được đánh giá nếu bạn thực sự * sử dụng * nó ... mà tất nhiên sẽ không xảy ra nếu kết quả là 'Không có gì'. Không phải là tuyệt vời để được lười biếng? –

3

Bạn cần phải sử dụng các biến kiểu scoped

{-# LANGUAGE ScopedTypeVariables #-} 

safeToEnum :: (Enum t, Bounded t) => Int -> Maybe t 
safeToEnum i = 
    if (i >= fromEnum (minBound :: t)) && (i <= fromEnum (maxBound :: t)) 
    then Just . toEnum $ i 
    else Nothing 

main = do 
    print $ (safeToEnum 1 :: Maybe Bool) 
    print $ (safeToEnum 2 :: Maybe Bool) 

Nếu không có nó, t nghĩa forall t. t.

+0

Vẫn nhận được 'Không thể suy luận (Bounded t1) từ ngữ cảnh()'. GHC 6.12.1. – sastanin

+0

OK. Tác phẩm này: 'safeToEnum :: forall t. (Enum t, Bounded t) => Int -> Có thể t'. Cảm ơn bạn. – sastanin

+1

Bạn có thể làm việc xung quanh sự cần thiết phải sử dụng các biến kiểu scoped bằng cách thêm một bộ phối hợp helper như: asArgTypeOf :: a -> f a -> a; asArgTypeOf = const và và hệ thống ống dẫn nó thông qua như safeToEnum i = r trong đó r = ... và sử dụng (minBound 'asArgTypeOf' r) và (maxBound' asArgTypeOf' r). –

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