haskellfrpreactive-banana

How to look back to the previous moment


I am reading a button's state (whether being pressed or not) every moment:

readButton :: IO Boolean
readButton = ...

main = do
    (add, fire) <- newAddHandler
    network <- compile (desc add)
    actuate network
    forever $ do
        buttonState <- readButton
        fire buttonState

desc addButtonEvent = do
    eButtonState <- fromAddHandler addButtonEvent
    ...

All the read states are stored into eButtonState in the network description desc.

The button is considered to be newly pressed when the current moment's state is 1 with the previous moment's being 0. So, if the event sequence was a list, the function would be written like this:

f :: [Bool] -> Bool
f (True:False:_) = True
f _              = False

I want to apply this function to eButtonState so I would know whether the button is newly pressed or not in the moment.

Is it ever possible? How would you do it? I would appreciate if there is a better or more common idea or method to achieve this goal.


Solution

  • Here is one way (this is a runnable demo):

    import Reactive.Banana
    import Reactive.Banana.Frameworks
    import Control.Monad
    import Control.Applicative -- Needed if you aren't on GHC 7.10.
    
    desc addDriver = do
        -- Refreshes the button state. Presumably fired by external IO.
        eButtonDriver <- fromAddHandler addDriver
        let -- Canonical repersentation of the button state.
            bButtonState = stepper False eButtonDriver
            -- Observes the button just before changing its state.
            ePreviousState = bButtonState <@ eButtonDriver
            -- Performs the test your f function would do.
            newlyPressed :: Bool -> Bool -> Bool
            newlyPressed previous current = not previous && current
            -- Applies the test. This works because eButtonDriver and
            -- ePreviousState are fired simultaneously.
            eNewlyPressed = unionWith newlyPressed
                ePreviousState eButtonDriver
            -- The same but more compactly, without needing ePreviousState.
            {-
            eNewlyPressed = newlyPressed <$> bButtonState <@> eButtonDriver
            -}
        reactimate (print <$> eNewlyPressed)
    
    main = do
        (addDriver, fireDriver) <- newAddHandler
        network <- compile (desc addDriver)
        actuate network
        -- Demo: enter y to turn the button on, and any other string to
        -- turn it off.
        forever $ do
            buttonState <- (== "y") <$> getLine
            fireDriver buttonState
    

    Notes: