[Haskell-beginners] FRP
Ertugrul Söylemez
es at ertes.de
Fri Sep 21 03:53:28 CEST 2012
Heinrich Apfelmus <apfelmus at quantentunnel.de> wrote:
> > Wire is also an Alternative, which allows concise and efficient
> > switching with very little cruft. The following wire renders "yes"
> > when the "keyDown Space" event happens and "no" otherwise:
> >
> > pure "yes" . keyDown Space <|> pure "no"
> >
> > Or with the OverloadedStrings extension:
> >
> > "yes" . keyDown Space <|> "no"
> >
> > All classic (non-wire) FRP implementations need switching or another
> > ad-hoc combinator for this. If you happen to need switching it's
> > also a lot simpler using wires:
> >
> > "please press space" . notE (keyDown Space) --> "thanks"
> >
> > This one waits for the Space key and then outputs "thanks" forever.
> > So far Netwire has the fastest and most elegant way of dealing with
> > events compared to all other libraries I have tried.
>
> These examples look neat!
>
> I'm a bit confused about the model you are using, though. If I
> understand that correctly, you don't distinguish between events and
> behaviors; rather, you are working with data streams in discrete time
> steps. Still, I don't quite understand.
First let me try to put reactive-banana's model into a data type of my
own, which you might call TimedZipStream. The name Behavior is easier,
so let me pick that one instead (Time is the type for time deltas):
newtype Behavior a =
Behavior {
stepBehavior :: Time -> Network (a, Behavior a)
}
This is not anywhere near how reactive-banana represents its behaviors,
but just a simplified and less powerful model. The argument is the time
delta to the last instant. Communication happens through the Network
monad and is opaque to the user. The type is an applicative functor
that represents values that can behave differently at each instant.
My model is similar to Yampas model, where instead of time-varying
values you have time-varying functions, so-called signal functions:
newtype SF a b =
SF {
stepSF :: Time -> a -> (b, SF a b)
}
This is a function from an 'a' to a 'b' that mutates over time. There
is a counterpart for classic behaviors, which is when the input type is
fully polymorphic:
time :: SF a Time
SF forms a family of applicative functors, but now there is a direct
path from one signal function to the next, because SF is itself a
category. No graph, no monad, just plain categorical composition.
Unfortunately to this day Yampa does not provide an Applicative
instance, so you have to use the Arrow interface, which is usually very
ugly.
The weak spot of both models is events. They need to be handled using
switchers and other combinators. Causality, simultaneity and choice all
need to be encoded explicitly. Event modifiers work outside of the
behavior level.
What makes Netwire different? Wire categories are encoded by the
following (simplified) type:
newtype Wire e a b =
Wire {
stepWire :: Time -> a -> (Either e b, Wire e a b)
}
Wires can choose not to output anything, but instead inhibit with a
value of type 'e'. Where i is an inhibiting wire the following
identities hold:
x . i = i
i . x = i
Furthermore now when 'e' is a Monoid Wire is a family of Alternative
functors with the following identities, where x and y produce and i, j
and ij' and inhibit:
x <|> y = x
i <|> y = y
i <|> j = ij'
The ij' wire also inhibits, but mappend-combines the inhibition values.
The empty wire always inhibits with mempty. The monoid is necessary for
the Category, Applicative and Alternative laws to hold.
> What is
>
> pure "yes" . keyDown Space <|> pure "no"
>
> supposed to mean? If it's a function Time -> String , how long does it
> have the "yes" value? 439.7 milliseconds? If it's an event, how often
> does the "no" event fire?
An event wire is a wire that acts like the identity wire when it
produces, but may choose to inhibit instead:
pure "yes" . keyDown Space
The 'keyDown Space' wire acts like the identity wire when the space key
is pressed, otherwise it inhibits. As a consequence of the above laws
the composition also inhibits. This is where (<|>) comes in:
pure "yes" . keyDown Space <|> pure "no"
When the left wire inhibits, the right wire takes over. By definition a
'pure' wire never inhibits. Notice that in this example I'm assuming
that 'keyDown' is a 'continuous event'. That's where behaviors are
mixed with events. An event can actually have a duration.
If 'keyDown' would be instantaneous without a duration you could use the
'holdFor' combinator:
pure "yes" . holdFor 1 (keyDown Space) <|> pure "no"
This would also work for a continuous 'keyDown' event wire. Then if you
press the space key for one second, "yes" is displayed for two seconds.
> Concerning the other example, I don't understand what the expression
>
> "please press space" . notE (keyDown Space)
>
> means. If it's a function, what value does it have when the key was
> pressed? If it's an event, how often does it "fire" the string value?
In this case it doesn't really matter if 'keyDown Space' has a duration
or not. As soon as it produces once, the switch happens and the next
wire takes over. There is another name for the (-->) combinator called
'andThen'.
x --> y
As soon as x inhibits, this combination switches to y.
Side note: Unlike classic FRP a wire category is not time-bound. In
fact in the current official release of Netwire (3.1.0) time is actually
an extension. In Netwire time is back as a primitive, because it makes
time-related wires (the analog to behaviors) much easier to write. I
have retained the flexibility that your wire can have a time frame
different from real time, though.
Greets,
Ertugrul
--
Not to be or to be and (not to be or to be and (not to be or to be and
(not to be or to be and ... that is the list monad.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://www.haskell.org/pipermail/beginners/attachments/20120921/6291fc2a/attachment.pgp>
More information about the Beginners
mailing list