{-| Contains miscellaneous utility functions such as functions for working with signals and signal generators. -} module FRP.Helm.Utilities ( -- * Angles radians, degrees, turns, -- * Applying (<|), (|>), -- * Random numbers random, randomR, -- * Composing constant, combine, lift, lift2, lift3, (<~), (~~), -- * Accumulating foldp, count, countIf, -- * DYEL? lift4, lift5, lift6, lift7, lift8 ) where import Control.Applicative ((<*>)) import Control.Monad ((>=>)) import FRP.Elerea.Simple import System.Random (Random, randomIO, randomRIO) {-| Converts radians into the standard angle measurement (radians). -} radians :: Double -> Double radians n = n {-| Converts degrees into the standard angle measurement (radians). -} degrees :: Double -> Double degrees n = n * pi / 180 {-| Converts turns into the standard angle measurement (radians). Turns are essentially full revolutions of the unit circle. -} turns :: Double -> Double turns n = 2 * pi * n {-| Forward function application, think of it as a inverted '($)'. Provided for easy porting from Elm. -} (|>) :: a -> (a -> b) -> b (|>) = flip ($) {-| Exactly the same as '($)', only there to make code using '(|>)' more consistent. -} (<|) :: (a -> b) -> a -> b (<|) = ($) {-| Creates a signal that never changes. -} constant :: a -> SignalGen (Signal a) constant = return . return {-| Combines a list of signals into a signal of lists. -} combine :: [SignalGen (Signal a)] -> SignalGen (Signal [a]) combine = sequence >=> return . sequence {-| Applies a function to a signal producing a new signal. This is a wrapper around the builtin 'fmap' function that automatically binds the input signal out of the signal generator. > render <~ Window.dimensions -} lift :: (a -> b) -> SignalGen (Signal a) -> SignalGen (Signal b) lift = fmap . fmap {-| Applies a function to two signals. -} lift2 :: (a -> b -> c) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) lift2 f a b = f <~ a ~~ b {-| Applies a function to three signals. -} lift3 :: (a -> b -> c -> d) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) -> SignalGen (Signal d) lift3 f a b c = f <~ a ~~ b ~~ c {-| Applies a function to four signals. -} lift4 :: (a -> b -> c -> d -> e) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) -> SignalGen (Signal d) -> SignalGen (Signal e) lift4 f a b c d = f <~ a ~~ b ~~ c ~~ d {-| Applies a function to five signals. -} lift5 :: (a -> b -> c -> d -> e -> f) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) -> SignalGen (Signal d) -> SignalGen (Signal e) -> SignalGen (Signal f) lift5 f a b c d e = f <~ a ~~ b ~~ c ~~ d ~~ e {-| Applies a function to six signals. -} lift6 :: (a -> b -> c -> d -> e -> f -> g) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) -> SignalGen (Signal d) -> SignalGen (Signal e) -> SignalGen (Signal f) -> SignalGen (Signal g) lift6 f a b c d e f1 = f <~ a ~~ b ~~ c ~~ d ~~ e ~~ f1 {-| Applies a function to seven signals. -} lift7 :: (a -> b -> c -> d -> e -> f -> g -> h) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) -> SignalGen (Signal d) -> SignalGen (Signal e) -> SignalGen (Signal f) -> SignalGen (Signal g) -> SignalGen (Signal h) lift7 f a b c d e f1 g = f <~ a ~~ b ~~ c ~~ d ~~ e ~~ f1 ~~ g {-| Applies a function to eight signals. -} lift8 :: (a -> b -> c -> d -> e -> f -> g -> h -> i) -> SignalGen (Signal a) -> SignalGen (Signal b) -> SignalGen (Signal c) -> SignalGen (Signal d) -> SignalGen (Signal e) -> SignalGen (Signal f) -> SignalGen (Signal g) -> SignalGen (Signal h) -> SignalGen (Signal i) lift8 f a b c d e f1 g h = f <~ a ~~ b ~~ c ~~ d ~~ e ~~ f1 ~~ g ~~ h {-| An alias for 'lift'. -} (<~) :: (a -> b) -> SignalGen (Signal a) -> SignalGen (Signal b) (<~) = lift infixl 4 <~ {-| Applies a function within a signal to a signal. This is a wrapper around the builtin '<*>' operator that automatically binds the input signal out of the signal generator. > render <~ Window.dimensions ~~ Window.position -} (~~) :: SignalGen (Signal (a -> b)) -> SignalGen (Signal a) -> SignalGen (Signal b) (~~) = (<*>) . fmap (<*>) infixl 4 ~~ {-| Creates a past-dependent signal that depends on another signal. This is a wrapper around the 'transfer' function that automatically binds the input signal out of the signal generator. This function is useful for making a render function that depends on some accumulated state. -} foldp :: (a -> b -> b) -> b -> SignalGen (Signal a) -> SignalGen (Signal b) foldp f ini = (>>= transfer ini f) {-| Creates a signal that counts the amount of times it has been sampled. -} count :: SignalGen (Signal Int) count = stateful 0 (+ 1) {-| Creates a signal that counts the amount of times an input signal has passed a predicate when sampled. -} countIf :: (a -> Bool) -> SignalGen (Signal a) -> SignalGen (Signal Int) countIf f = foldp (\v c -> c + fromEnum (f v)) 0 {-| Creates a signal of a random number. -} random :: Random a => SignalGen (Signal a) random = effectful randomIO {-| Creates a signal of a random number based on the given range. -} randomR :: Random a => (a, a) -> SignalGen (Signal a) randomR = effectful . randomRIO