Reputation: 943
I am hacking around in Helm trying to create a stateful signal type that will carry additional info on whether the value has changed since the last sampling. I have managed to make the new signal type an instance of Functor
but I am struggling to define the <*>
function of Applicative
which would allow me to lift multiple signals into one function.
The only signal I have defined so far is the constant
signal that would have a Changed x
on the initial sampling and an Unchanged x
thereafter. Additional signals would be more complex and use Elerea.stateful
to determine whether the value has changed.
lift2
wouldn't work in the code below because <*>
hasn't been defined yet.
import qualified FRP.Elerea.Simple as Elerea
import Control.Applicative
data Event a = Changed a | Unchanged a
instance Functor Event where
fmap f (Changed a) = Changed (f a)
fmap f (Unchanged a) = Unchanged (f a)
instance Applicative Event where
pure = Unchanged
(Changed f) <*> (Changed x) = Changed (f x)
(Changed f) <*> (Unchanged x) = Changed (f x)
(Unchanged f) <*> (Changed x) = Changed (f x)
(Unchanged f) <*> (Unchanged x) = Unchanged (f x)
data Signal a = Signal (Elerea.SignalGen (Elerea.Signal (Event a)))
instance Functor Signal where
fmap f (Signal x) = Signal ((fmap . fmap . fmap) f x)
instance Applicative Signal where
pure = Signal . pure . pure . pure
--(<*>) = ??
{-| Creates a signal that never changes. -}
constant :: a -> Signal a
constant x = Signal $ Elerea.stateful (Changed x) (\_ -> Unchanged x)
lift :: (a -> b) -> Signal a -> Signal b
lift = fmap
lift2 :: (a -> b -> c) -> Signal a -> Signal b -> Signal c
lift2 f a b = fmap f a <*> b
Full code that I am working with currently is on Github.
Upvotes: 2
Views: 136
Reputation: 1667
The signature of pure . pure . pure
is:
(Applicative f, Applicative f1, Applicative f2) => a -> f (f1 (f2 a))
So you just want to lift the <*>
through two additional applicative layers. You need a function with the signature:
(Applicative f, Applicative f1, Applicative f2) => f (f1 (f2 (a -> b))) -> f (f1 (f2 a)) -> f (f1 (f2 b))
This function can be made by applying liftA2
twice, so please try this:
(Signal f) <*> (Signal x) = Signal $ (liftA2 (liftA2 (<*>))) f x
Upvotes: 1