私は現在、reactive-banana を使用して FRP を学習しており、ランダム関数のストリームを作成したいと考えていました。私はこれを思いついた:
-- | 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
それはうまくいくようです、私はこのように使うことができます:
randFuncs = mkRandom rnd (pulse 1000 time)
some = ($ (0,10::Int)) <$> randFuncs
しかしもちろん、そのストリームを共有して別のタイプの数値を生成しようとすると、次のようになります。
some2 = ($ (0,10::Double)) <$> randFuncs
タイプチェッカーが不平を言っていますが、それは理解しています。次に、関数を次のように一般化しようとしました。
mkRandom :: (RandomGen g) => g -> Event t b -> Event t (forall a. Random a => (a,a) -> a)
その後、GHC は不正なポリモーフィック シグネチャと、ImpredicativeTypes を有効にするかどうかについて不平を言いました。私はそれを実行し、かなり長い間すべてに注釈を付けて機能させようとしましたが、GHC は常に型に一致しないと不満を漏らしていました。
私の質問は - 私がやりたいことをすることは可能ですか? そのためにImpredicativeTypesが本当に必要ですか、それとも間違っていますか?
RankNTypes で十分だと思いましたが、そのような拡張機能の経験はまだありません。
前もって感謝します!
編集:
記録のために、役立つ応答に基づく私の解決策は次のとおりです。
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