I am currently learning FRP with reactive-banana and wanted to create a stream of random functions. I've come up with this:
-- | take number generator, and some pulse event stream, generate random function stream
mkRandom :: (Random a,RandomGen g) => g -> Event t b -> Event t ((a,a) -> a)
mkRandom rng es = (\f -> \r -> fst $ f r) <$> (accumE first $ next <$> es)
where first = flip randomR rng
next _ prev range = randomR range g
where (a,g) = prev range
It seems to work, I can use it like this:
randFuncs = mkRandom rnd (pulse 1000 time)
some = ($ (0,10::Int)) <$> randFuncs
But, of course, when I try to share that stream to generate numbers of a different type:
some2 = ($ (0,10::Double)) <$> randFuncs
The type checker complains, which I understand. Then I tried to generalize the function to the following:
mkRandom :: (RandomGen g) => g -> Event t b -> Event t (forall a. Random a => (a,a) -> a)
Then GHC complained about illegal polymorphic signature and whether I'd like to enable ImpredicativeTypes. I did it and for quite a while tried to annotate everything to make it work, but GHC always complained that it could not match the types.
My question is - is it possible to do what I want? Do I really need ImpredicativeTypes for that or am I just doing it wrong?
I thought RankNTypes should be enough for it, but I have no experience with such extensions yet.
Thanks in advance!
EDIT:
For the record, now my solution based on the helpful response is:
newtype RandomSource = Rand { getRand :: forall a. (Random a) => (a,a) -> [a] }
-- | take number generator and some pulse event stream, generate randomness stream
mkRandom :: RandomGen g => g -> Event t a -> Behavior t RandomSource
mkRandom rng es = fst <$> (accumB (next id (id,rng)) $ next <$> es)
where next _ (_,rng) = (Rand $ flip randomRs g1, g2)
where (g1,g2) = split rng
-- | take a rand. source, a range and a pulse, return stream of infinite lists of random numbers
randStream :: Random a => Behavior t RandomSource -> (a,a) -> Event t b -> Event t [a]
randStream funcs range pulse = ($ range) . getRand <$> funcs <@ pulse
master
) will simplify the types in non-trivial and substantial ways, such as not requiringRankNTypes
(in some scenarios?), so you might give it a try. – Architecturalt
. The question at hand is unrelated to this. – Aton