Skip to content

Instantly share code, notes, and snippets.

@i-am-tom
Last active August 24, 2017 19:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save i-am-tom/ef4541d0a8f07e70cca40339c32ceb43 to your computer and use it in GitHub Desktop.
Save i-am-tom/ef4541d0a8f07e70cca40339c32ceb43 to your computer and use it in GitHub Desktop.
Issues around FRP.Event due to circular dependencies.
module Main where
import Prelude
import Control.Apply (lift2)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Data.Maybe (Maybe(Just, Nothing))
import Data.Tuple (Tuple(Tuple))
import FRP (FRP)
import FRP.Event (Event, create, fold, mapMaybe, subscribe, withLast)
import FRP.Event.Keyboard (down)
import FRP.Event.Time (withTime)
-- Create a stream whose unblocked events may lead to blocking. Pressing any key leads to
-- a stack overflow in the console, not any useful output.
main :: forall eff. Eff (console :: CONSOLE, frp :: FRP | eff) Unit
main = do
state <- create
let
-- | Remove any neighbouring duplicates
nub :: forall a. Eq a => Event a -> Event a
nub = withLast >>> mapMaybe \{ now, last } ->
case last of Nothing -> Just now
Just xs -> if now == xs
then Nothing
else Just xs
-- Take two streams, block the second depending on a predicate on both.
guard :: forall f p x. Applicative f => (p -> x -> Boolean) -> f p -> f x -> f (Maybe x)
guard f = lift2 \p x -> if f p x then Just x else Nothing
-- Every keypress, timestamped
stream :: Event (Tuple Int Int) -- (start time, key)
stream = (\{ time, value } -> Tuple time value) <$> withTime down
-- Every keypress, timestamped, which happened AFTER the expry time (state.event's latest value)
safe :: Event (Maybe (Tuple Int Int)) -- Unblocked (start, key) events
safe = guard (\expiry (Tuple start _) -> expiry < start) state.event stream
reducer :: Tuple Int Int -> Tuple Int Int -> Tuple Int Int
reducer (Tuple _ total) (Tuple start new) = Tuple start (total + new)
-- The cumulative keycode sum, timestamped. (This represents "some dependence in the process").
-- Note that we can't do this earlier - we don't want to include "blocked" keypresses.
expiries :: Event (Tuple Int Int) -- (start, pause)
expiries = fold reducer (mapMaybe id safe) (Tuple 0 0)
-- If we get this far, we have a valid event, and can block the stream.
subscribe expiries \(Tuple start pause) -> do
state.push (start + pause)
log $ "Starting block for " <> show pause <> "ms"
-- If we _don't_ get that far, we tell the user that something broke.
subscribe (nub safe) $ case _ of
Just x -> pure unit -- We don't care
Nothing -> log "THIS ACTION IS BLOCKED!"
state.push 0 -- Open the doors!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment