Reputation: 731
I've defined the following Data Type:
data NewBool = Truth | Lie deriving (Show)
and I've created a function which should return a random NewBool-value
giveMeBool :: IO()
giveMeBool = do
bool <- randomIO :: IO NewBool
putStrLn("Your random boolean is"++ show bool)
I've read here that I have to make NewBool an instance of Random to use randomIO. So I've done this:
instance Random NewBool where
random g = case random g of
(r,g') | r < (1/2)= (Truth, g')
| otherwise= (Lie, g')
Sincerely it's just a copy&paste of what I've found in another post, but I don't understand exactly how it works? How is the value and Type of r defined? Can anyone help me? Thanks ;)
Upvotes: 1
Views: 345
Reputation: 74374
Since random
has the type (essentially)
random :: Random a => StdGen -> (a, StdGen)
if you pass it the StdGen
you have, g
, it'll give you a value of any type instantiating the Random
type. The compiler guesses at what type you need by how you use it. In your case, it gets used in the fragment
r < (1/2)
and we can examine the types of each of these pieces
(<) :: Ord a => a -> a -> Bool
(1/2) :: Fractional a => a
which together imply that the full type of r
is
r :: (Random a, Fractional a, Ord a) => a
This isn't enough yet for Haskell to proceed, but since this is a common enough situation in numeric types (since numeric literals resolve to ambiguous types, not concrete numeric types) Haskell has a "defaulting" system. In particular, the following default
configuration is in effect (by default)
default (Integer, Double)
which means that if you have an ambiguous type, constrained by classes, and either Integer
or Double
can satisfy those classes then, in that order, these concrete types are substituted in. In the case of r
, Double
instantiates Random
, Ord
, and Fractional
so the compiler picks r :: Double
.
Finally, we consider the Random
instance for Double
which just so happens to pick random Double
values on the interval (0,1)
. This means that there's a 50% chance of it being greater than (1/2)
and thus each of the Truth
and Lie
constructors will be chosen with equal odds.
Finally, for really implementing Random NewBool
it's possibly better to bootstrap the instance off the very similar instance available with Bool
.
instance Random NewBool where
random g = case random g of
(True, g') -> (Truth, g')
(False, g') -> (Lie, g')
though this only works if you don't want to modify the behavior of the random generator from that of Random Bool
. However, take note that a similar type resolution is occurring here—except it's far simplified since I'm matching on patterns True
and False
which immediately mean that the return type of random g
must be (Bool, StdGen)
.
Upvotes: 6