6

私は現在、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
4

1 に答える 1

7

ImpredicativeTypesは、実際にはサポートも保守もされていない非常に脆弱な拡張機能であるため、GHC の新しいバージョンではさらに壊れ続けています。

はるかに優れた作業オプションは、ラッパーRankNTypesと一緒に使用することです:newtype

newtype PolyRandFun = PR { getPR :: forall a. Random a => (a,a) -> a) }

これには、newtype コンストラクターを明示的にラップおよびアンラップする必要がありますが、それ以外の場合は、このような多相関数を渡すのに問題なく機能します。

残念ながら、この場合、別の問題が予想されます。異なるインスタンスは、異なるRandom aの乱数発生器を使用します。たとえば、結果を構築するために生成されるプリミティブ乱数の量は、範囲のサイズに依存します。したがって、実際に関数を呼び出すときに使用される型と範囲を知らずに、次を取得することはできません。IntegerIntegerg

幸いなことに、System.RandomAPI にはこれを回避できる関数がありsplitます。複数のランダム値を完全に個別に生成する必要がある場合に、サブ計算に渡すことができる新しいランダム ジェネレータを提供します。

于 2015-10-07T14:27:52.603 に答える