I want to make all types that are instances of Enum and Bounded also an instances of Random. The following code does this and should work (with the appropriate extensions enabled):
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)
But I am aware this is bad style because instance (Enum r, Bounded r) => Random r creates an instance for all r, just with type checks for Enum and Bounded rather than just putting an instance on types that are Enum and Bounded. This effectively means I'm defining an instance for all types :(.
The alternate is that I have to write standalone functions that give me the behavior I want and write some boilerplate for each type I want to be an instance of 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
rand开发者_开发问答omR = randomBoundedEnumR
random = randomBoundedEnum
data Hygiene = Spotless | Normal | Scruffy | Grubby | Flithy
deriving (Enum, Bounded)
-- Boilerplatey, duplication :(
instance Random Hyigene where
randomR = randomBoundedEnumR
random = randomBoundedEnum
Are there any better alternatives? How should I manage this problem? Should I not even be attempt this at all? Am I overly worried about boilerplate?
Yes, as I just answered to a slightly related question, you can use a newtype wrapper - this is the common and safe way to make such instances without annoying the entire community.
newtype RandomForBoundedEnum a = RfBE { unRfBE :: a}
instance (Enum a, Bounded a) => Random (RandomForBoundedEnum a) where
....
In this manner, users who want to use this instance simply need to wrap (or unwrap) the calls:
first unRfBE . random $ g :: (Side, StdGen)
加载中,请稍侯......
精彩评论