2010-10-15 66 views
4

我想讓所有類型爲EnumBounded的實例也是Random的實例。下面的代碼執行此操作並應該工作(並啓用了相應的擴展名):多態類受限實例

import System.Random 

instance (Enum r, Bounded r) => Random r where 
    randomR (hi, lo) = inFst toEnum . randomR (fromEnum hi, fromEnum lo) 
     where inFst f (x,y) = (f x, y) 
    random = randomR (maxBound, minBound) 

但我知道這是不好的風格,因爲instance (Enum r, Bounded r) => Random r所有r創建一個實例,只是類型檢查EnumBounded而不是隻需在EnumBounded的類型上加入實例即可。這實際上意味着我正在爲所有類型的定義一個實例:(

備用的是,我必須寫獨立的功能,讓我的行爲,我想,寫一些樣板每種類型我想成爲的Random一個實例:

randomBoundedEnum :: (Enum r, Bounded r, RandomGen g) => g -> (r, g) 
randomBoundedEnum = randomRBoundedEnum (minBound, maxBound) 

randomBoundedEnumR :: (Enum r, Bounded r, RandomGen g) => (r, r) -> g -> (r, g) 
randomBoundedEnumR (hi, lo) = inFst toEnum . randomR (fromEnum hi, fromEnum lo) 
    where inFst f (x,y) = (f x, y) 

data Side = Top | Right | Bottom | Left 
    deriving (Enum, Bounded) 

-- Boilerplatey :( 
instance Random Side where 
    randomR = randomBoundedEnumR 
    random = randomBoundedEnum 

data Hygiene = Spotless | Normal | Scruffy | Grubby | Flithy 
    deriving (Enum, Bounded) 

-- Boilerplatey, duplication :(
instance Random Hyigene where 
    randomR = randomBoundedEnumR 
    random = randomBoundedEnum 

有沒有更好的方法?我應該如何處理這個問題?我甚至不應該試圖這樣做嗎?我過分擔心樣板嗎?

回答

8

是的,正如我剛剛回答slightly related question時所說的那樣,您可以使用一種新類型包裝器 - 這是一種常見而安全的方法,可以使這些實例不會影響整個社區。

newtype RandomForBoundedEnum a = RfBE { unRfBE :: a} 
instance (Enum a, Bounded a) => Random (RandomForBoundedEnum a) where 
    .... 

這樣,誰想要使用這個實例的用戶只需要簡單地包裹(或解)的電話:

first unRfBE . random $ g :: (Side, StdGen)