{-# LANGUAGE ScopedTypeVariables #-} module FRP.Helm.Random ( range, float, floatList ) where import Control.Applicative (pure) import Control.Monad (liftM, join, replicateM) import FRP.Elerea.Param hiding (Signal) import qualified FRP.Elerea.Param as Elerea (Signal) import FRP.Helm.Signal import FRP.Helm.Sample import FRP.Helm.Engine import System.Random (Random, randomRIO) {-| Given a range from low to high and a signal of values, this produces a new signal that changes whenever the input signal changes. The new values are random number between 'low' and 'high' inclusive. -} range :: Int -> Int -> Signal a -> Signal Int range x y = rand (x,y) {-| Produces a new signal that changes whenever the input signal changes. The new values are random numbers in [0..1). -} float :: Signal a -> Signal Float float = rand (0,1) {-| A utility signal that does the work for 'float' and 'range'. -} rand :: (Random a, Num a) => (a, a) -> Signal b -> Signal a rand limits s = Signal $ do s' <- signalGen s rs :: Elerea.Signal (SignalGen Engine (Elerea.Signal a)) <- randomGens limits s' r :: Elerea.Signal (Elerea.Signal a) <- generator rs transfer2 (pure 0) update_ s' (join r) where update_ :: (Random a, Num a) => p -> Sample b -> a -> Sample a -> Sample a update_ _ new random old = case new of Changed _ -> Changed random Unchanged _ -> Unchanged $ value old randomGens :: (Random a, Num a) => (a,a) -> Elerea.Signal (Sample b) -> SignalGen p (Elerea.Signal (SignalGen p (Elerea.Signal a))) randomGens l = transfer (return (return 0)) (makeGen l) makeGen ::(Random a, Num a) => (a,a) -> p -> Sample b -> SignalGen p (Elerea.Signal a) -> SignalGen p (Elerea.Signal a) makeGen l _ new _ = case new of Changed _ -> effectful $ randomRIO l Unchanged _ -> return $ return 0 {-| Produces a new signal of lists that changes whenever the input signal changes. The input signal specifies the length of the random list. Each value is a random number in [0..1). -} floatList :: Signal Int -> Signal [Float] floatList s = Signal $ do s' <- signalGen s fl :: Elerea.Signal (SignalGen Engine (Elerea.Signal [Float])) <- floatListGens s' ss :: Elerea.Signal (Elerea.Signal [Float]) <- generator fl transfer2 (pure []) update_ s' (join ss) where floatListGens :: Elerea.Signal (Sample Int) -> SignalGen p (Elerea.Signal (SignalGen p (Elerea.Signal [Float]))) floatListGens = transfer (return (return [])) makeGen makeGen _ new _ = case new of Changed n -> liftM sequence $ replicateM n $ effectful $ randomRIO (0,1) Unchanged _ -> return (return []) update_ _ int new old = case int of Changed _ -> Changed new Unchanged _ -> Unchanged $ value old