2

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.

kasbah
  • 903
  • 5
  • 23
  • 1
    Why does `Signal` incorporate `SignalGen`? – John L Oct 09 '14 at 03:14
  • So Elerea defines `Elerea.Signal` and `Elerea.SignalGen`. Signals in Elerea are eventless and just constant streams of values. I am trying to define a new Signal type, closer to the [Elm](http://elm-lang.org) idea of Signals that can be used to determine if the main Element needs re-calculating and re-rendering (when the values have changed since the last sampling). – kasbah Oct 09 '14 at 03:24
  • Also see this [Helm issue](https://github.com/switchface/helm/issues/64) (the bounty was created by me). – kasbah Oct 09 '14 at 04:10
  • Maybe more clear: `Elerea.stateful` returns a `Elerea.SignalGen (Elerea.Signal a)` and I am trying to wrap that. – kasbah Oct 09 '14 at 07:02

1 Answers1

1

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
Reite
  • 1,677
  • 10
  • 12
  • That works! Thank you very much for the answer and explaining your thought process in getting there. Note to self: always look at the types. – kasbah Oct 09 '14 at 21:16