Reputation: 1309
I'm trying to learn Haskell in the most "Haskell way" possible, so I'm trying to avoid things like do
s and let
s that sort of obscure what's actually going on.
So I have a data type called Org
that in is defined as so:
data Org = Org
{ pos :: Pos2D
, color :: Color
} deriving (Show)
where Color
and Pos2D
are defined as so:
-- Not actually defined like this, it's Graphics.Gloss.Data.Color
data Color = Color Float Float Float Float
data Pos2D = Pos2D Float Float
Now I've implemented UniformRange
for Pos2D
and Uniform
for Color
as so:
instance UniformRange Pos2D
where
uniformRM (Pos2D x1 y1, Pos2D x2 y2) g = Pos2D
<$> uniformRM (x1, x2) g
<*> uniformRM (y1, y2) g
instance Uniform Color
where
uniformM g = G.makeColor
<$> uniformRM (0.0, 1.0) g
<*> uniformRM (0.0, 1.0) g
<*> uniformRM (0.0, 1.0) g
<*> return 1.0
Given a tuple of Pos2D
(representing the corners of the bounds), it will return a random Pos2D
distributed uniformly between those. And for Color
, it will return a random color with uniform R, G, and B values, but with a fixed alpha = 255 (1.0).
The question is now, how do I make an org?
randomOrg :: RandomGen g => g -> Org
randomOrg = -- ...
where
gen = mkStdGen 1337
The issue I'm having is that uniformR
and uniformM
take a g
and return a tuple of (a, g)
. So I'm not sure what the cleanest, most "Haskelly" way would be to actually chain these calls together to create the Org.
Any thoughts? Thanks,
Edit:
I know that I can do this:
randomOrg :: RandomGen g => g -> Org
randomOrg g = Org pos color
where
(pos, g1) = uniformR (Pos2D 0 0, Pos2D 720 480) g
(color, g2) = uniform g1
But I'm not a fan of that for obvious reasons.
Edit 2:
Ok so after some thinking, I think the function I need might need to look something like this?
pUniform
:: (Uniform a, RandomGen g)
=> (a -> b, g) -> (g -> (a, g)) -> (b, g)
But I'm not sure.
Edit 3:
Now I'm thinking a functor might be relvant:
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
So if we take some function (a->b)
(which could be for example the Org
constructor), and we have some a
wrapped in the context f
, which would be like (a, g)
then we can produce a b
wrapped in the context, which would be (b, g)
. Seems right to me, but I'm not sure how to translate that into actual code.
Upvotes: 2
Views: 386
Reputation: 8477
As outlined here, the best way of doing this is by using the new monadic interface:
randomOrg :: StatefulGen g m => g -> m Org
randomOrg g = Org <$> uniformR (Pos2D 0 0, Pos2D 720 480) g <*> uniform g
Which you can then specialise to a pure stateful function:
randomOrg' :: RandomGen g => g -> (Org, g)
randomOrg' g = runStateGen g randomOrg
Though this approach is more general, so you can also run it e.g. in IO
using mutable state:
randomOrg'' :: RandomGen g => IOGenM g -> IO Org
randomOrg'' = randomOrg
(Warning: these should work, but I haven’t double-checked it myself; please tell me if you find any lurking errors!)
Upvotes: 4