2010-10-15 3 views
4

Я хочу сделать все типы, которые являются экземплярами Enum и Bounded также экземплярами 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, только с проверкой типа для Enum и Bounded, а не просто введите экземпляр для типов, которые являются Enum и Bounded. Это фактически означает, что я определяю экземпляр для всех типов :(.

Альтернативный том, что я должен написать автономные функции, которые дают мне поведение я хочу и написать какое-нибудь шаблонный для каждого типа я хочу быть экземпляром 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 - это общий и безопасный способ сделать такие экземпляры без раздражения всего сообщества.

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

Таким образом, пользователи, которые хотят использовать этот экземпляр просто нужно обернуть (или разворачивать) вызовы:

first unRfBE . random $ g :: (Side, StdGen) 
Смежные вопросы