{-----------------------------------------------------------------------------
Reactive Banana
Class interface + Semantic model
------------------------------------------------------------------------------}{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, EmptyDataDecls,
MultiParamTypeClasses #-}moduleReactive.Banana.Model(-- * Synopsis-- | Combinators for building event networks and their semantics.-- * Core CombinatorsmoduleControl.Applicative,FRP(..),Event,Behavior,-- $classes-- * Derived CombinatorswhenE,filterJust,mapAccum,Apply(..),-- * Model implementationModel,Time,interpretTime,interpret,)whereimportControl.ApplicativeimportqualifiedData.ListimportData.MaybeimportPreludehiding(filter)importData.Monoid{-----------------------------------------------------------------------------
Class interface
------------------------------------------------------------------------------}datafamilyEventf::*->*datafamilyBehaviorf::*->*{- | The 'FRP' class defines the primitive API for functional reactive programming.
Each instance 'f' defines two type constructors @Event f@ and @Behavior f@
and corresponding combinators.
@Event f a@ represents a stream of events as they occur in time.
Semantically, you can think of @Event f a@ as an infinite list of values
that are tagged with their corresponding time of occurence,
> type Event f a = [(Time,a)]
@Behavior f a@ represents a value that varies in time. Think of it as
> type Behavior f a = Time -> a
While these type synonyms are the way you should think about
'Behavior' and 'Event', they are a bit vague for formal manipulation.
To remedy this, the library provides a very simple model implementation,
called 'Model'.
This model is /authoritative/: every instance of the 'FRP' class /must/
give the same results as the model when observed with the 'interpret' function.
Note that this must also hold for recursive and partial definitions
(at least in spirit, I'm not going to split hairs over @_|_@ vs @\\_ -> _|_@).
Concerning time and space complexity, the model is not authoritative, however.
Implementations are free to be much more efficient.
Minimal complete definition of the 'FRP' class: One of 'filter' or 'filterApply'
and one of 'accumB' or 'stepper'.
-}class(Functor(Eventf),Functor(Behaviorf),Applicative(Behaviorf))=>FRPfwhere-- | Event that never occurs.-- Think of it as @never = []@.never::Eventfa-- | Merge two event streams of the same type.-- In case of simultaneous occurrences, the left argument comes first.-- Think of it as---- > union ((timex,x):xs) ((timey,y):ys)-- > | timex <= timey = (timex,x) : union xs ((timey,y):ys)-- > | timex > timey = (timey,y) : union ((timex,x):xs) ysunion::Eventfa->Eventfa->Eventfa-- | Apply a time-varying function to a stream of events.-- Think of it as-- -- > apply bf ex = [(time, bf time x) | (time, x) <- ex]apply::Behaviorf(a->b)->Eventfa->Eventfb-- | Allow all events that fulfill the predicate, discard the rest.-- Think of it as-- -- > filterE p es = [(time,a) | (time,a) <- es, p a]filterE::(a->Bool)->Eventfa->Eventfa-- | Allow all events that fulfill the time-varying predicate, discard the rest.-- It's a slight generalization of 'filterE'.filterApply::Behaviorf(a->Bool)->Eventfa->Eventfa-- Accumulation.-- Note: all accumulation functions are strict in the accumulated value!-- acc -> (x,acc) is the order used by unfoldr and State-- | Construct a time-varying function from an initial value and -- a stream of new values. Think of it as---- > stepper x0 ex = \time -> last (x0 : [x | (timex,x) <- ex, timex < time])-- -- Note that the smaller-than-sign in the comparision @timex < time@ means -- that the value of the behavior changes \"slightly after\"-- the event occurrences. This allows for recursive definitions.-- -- Also note that in the case of simultaneous occurrences,-- only the last one is kept.stepper::a->Eventfa->Behaviorfa-- | The 'accumB' function is similar to a /strict/ left fold, 'foldl''.-- It starts with an initial value and combines it with incoming events.-- For example, think---- > accumB "x" [(time1,(++"y")),(time2,(++"z"))]-- > = stepper "x" [(time1,"xy"),(time2,"xyz")]-- -- Note that the value of the behavior changes \"slightly after\"-- the events occur. This allows for recursive definitions.accumB::a->Eventf(a->a)->Behaviorfa-- | The 'accumE' function accumulates a stream of events.-- Example:---- > accumE "x" [(time1,(++"y")),(time2,(++"z"))]-- > = [(time1,"xy"),(time2,"xyz")]---- Note that the output events are simultaneous with the input events,-- there is no \"delay\" like in the case of 'accumB'.accumE::a->Eventf(a->a)->Eventfa-- implementation filterfilterEp=filterApply(purep)filterApplybp=fmapsnd.filterEfst.apply((\pa->(pa,a))<$>bp)-- implementation accumulationaccumBacc=stepperacc.accumEaccstepperacc=accumBacc.fmapconst{-$classes
/Further combinators that Haddock can't document properly./
> instance FRP f => Monoid (Event f a)
The combinators 'never' and 'union' turn 'Event' into a monoid.
> instance FPR f => Applicative (Behavior f)
'Behavior' is an applicative functor. In particular, we have the following functions.
> pure :: FRP f => a -> Behavior f a
The constant time-varying value. Think of it as @pure x = \\time -> x@.
> (<*>) :: FRP f => Behavior f (a -> b) -> Behavior f a -> Behavior f b
Combine behaviors in applicative style.
Think of it as @bf \<*\> bx = \\time -> bf time $ bx time@.
-}instanceFRPf=>Monoid(Eventfa)wheremempty=nevermappend=union{-----------------------------------------------------------------------------
Derived Combinators
------------------------------------------------------------------------------}-- | Variant of 'filterApply'.whenE::FRPf=>BehaviorfBool->Eventfa->EventfawhenEbf=filterApply(const<$>bf)-- | Variant of 'filterE'. Keep only the 'Just' values.filterJust::FRPf=>Eventf(Maybea)->EventfafilterJust=fmap(maybeerrid).filterEisJustwhereerr=error"Reactive.Banana.Model.filterJust: Internal error. :("-- | Efficient combination of 'accumE' and 'accumB'.mapAccum::FRPf=>acc->Eventf(acc->(x,acc))->(Eventfx,Behaviorfacc)mapAccumaccef=(fst<$>e,stepperacc(snd<$>e))wheree=accumE(undefined,acc)((.snd)<$>ef)infixl4<@>,<@-- | Class for overloading the 'apply' function.class(Functorf,Functorg)=>Applyfgwhere-- | Infix operation for the 'apply' function, similar to '<*>'(<@>)::f(a->b)->ga->gb-- | Convenience function, similar to '<*'(<@)::fa->gb->gaf<@g=(const<$>f)<@>ginstanceFRPf=>Apply(Behaviorf)(Eventf)where(<@>)=apply{-----------------------------------------------------------------------------
Semantic model
------------------------------------------------------------------------------}-- | The type index 'Model' represents the model implementation.-- You are encouraged to look at the source code!-- (If there is no link to the source code at every type signature,-- then you have to run @cabal@ with @--hyperlink-source@ flag.)dataModel-- Stream of events. Simultaneous events are grouped into lists.newtypeinstanceEventModela=E{unE::[[a]]}-- Stream of values that the behavior takes.newtypeinstanceBehaviorModela=B{unB::[a]}instanceFunctor(EventModel)wherefmapf=E.map(mapf).unEinstanceApplicative(BehaviorModel)wherepurex=B$repeatxbf<*>bx=B$zipWith($)(unBbf)(unBbx)instanceFunctor(BehaviorModel)wherefmap=liftAinstanceFRPModelwherenever=E$repeat[]unione1e2=E$zipWith(++)(unEe1)(unEe2)filterApplybp=E.zipWith(\pxs->Data.List.filterpxs)(unBbp).unEapplyb=E.zipWith(\fxs->mapfxs)(unBb).unEstepperx=B.scanlgox.unEwheregoxe=last(x:e)accumEacc=E.accumE'acc.unEwhereaccumE'acc[]=[]accumE'acc(e:es)=e':accumE'acc'eswherevals=scanl'(flip($))accee'=tail$valsacc'=lastvals-- strict version of scanlscanl'::(a->b->a)->a->[b]->[a]scanl'fxys=x:caseysof[]->[]y:ys->letz=fxyinz`seq`scanl'fzys-- | Slightly simpler interpreter that does not mention 'Time'.-- Returns lists of event values that occur simultaneously.interpret::(EventModela->EventModelb)->[a]->[[b]]interpretf=unE.f.E.map(:[])typeTime=Double-- | Interpreter that corresponds to your mental model.interpretTime::(EventModela->EventModelb)->[(Time,a)]->[(Time,b)]interpretTimefxs=concat.zipWithtagtimes.interpretf.mapsnd$xswheretimes=mapfstxstagtxs=map(\x->(t,x))xs{-----------------------------------------------------------------------------
Example: Counter that can be decreased
------------------------------------------------------------------------------}example::FRPf=>Eventf()->EventfIntexampleedec=apply((\c_->c)<$>bcounter)ecandecreasewherebcounter=accumB10$(subtract1)<$ecandecreaseecandecrease=whenE((>0)<$>bcounter)edectestModel=interpretexample$replicate15()-- > testModel-- [[10],[9],[8],[7],[6],[5],[4],[3],[2],[1],[],[],[],[],[]]example2::FRPf=>Eventf()->EventfIntexample2e=apply(const<$>b)ewhereb=accumB0((+1)<$e)