
On 09/17/2012 12:09 PM, Kim-Ee Yeoh wrote:
Ever wired up some simple electronic circuits? Take a flip-flop for instance. Suppose each electronic component was a Haskell function. How would you design their types in order for the whole program be functionally equivalent?
FRP is a theory for the design and construction of programs along such lines.
-- Kim-Ee
Which FRP framework (i.e., Haskell package) is the best one to play with for someone still trying to grasp the basics of FRP? -- frigidcode.com indicium.us

I have found Reactive Banana to be the most accessible so far. Still
struggling with some of the language syntax though.
Cheers,
Darren
On Tue, Sep 18, 2012 at 2:20 PM, Christopher Howard
On 09/17/2012 12:09 PM, Kim-Ee Yeoh wrote:
Ever wired up some simple electronic circuits? Take a flip-flop for instance. Suppose each electronic component was a Haskell function. How would you design their types in order for the whole program be functionally equivalent?
FRP is a theory for the design and construction of programs along such lines.
-- Kim-Ee
Which FRP framework (i.e., Haskell package) is the best one to play with for someone still trying to grasp the basics of FRP?
-- frigidcode.com indicium.us
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Darren Grant wrote:
I have found Reactive Banana to be the most accessible so far. Still struggling with some of the language syntax though.
Are you struggling with the Haskell syntax, or with FRP in general or with any particular example? Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

Primarily Haskell syntax.
On Wed, Sep 19, 2012 at 8:18 AM, Heinrich Apfelmus
Darren Grant wrote:
I have found Reactive Banana to be the most accessible so far. Still struggling with some of the language syntax though.
Are you struggling with the Haskell syntax, or with FRP in general or with any particular example?
Best regards, Heinrich Apfelmus
-- http://apfelmus.nfshost.com
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Thu, Sep 20, 2012 at 2:09 AM, Darren Grant
Primarily Haskell syntax.
Would you like to try an experiment with me? Post one or two lines of code
that's been really challenging for you, and let's see if we can't figure it
out. The bar I'll set myself is not just explaining how it works but also
figuring out to /say/ the code in plain English. How's that?
-- Kim-Ee
On Thu, Sep 20, 2012 at 2:09 AM, Darren Grant
Primarily Haskell syntax.
On Wed, Sep 19, 2012 at 8:18 AM, Heinrich Apfelmus
wrote: Darren Grant wrote:
I have found Reactive Banana to be the most accessible so far. Still struggling with some of the language syntax though.
Are you struggling with the Haskell syntax, or with FRP in general or with any particular example?
Best regards, Heinrich Apfelmus
-- http://apfelmus.nfshost.com
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Christopher Howard
Which FRP framework (i.e., Haskell package) is the best one to play with for someone still trying to grasp the basics of FRP?
In terms of tutorials and related blog posts I currently recommend reactive-banana. In terms of power and elegance I recommend Netwire. The main difference is that reactive-banana is both simple and easy. It implements classic FRP with the usual notion of behaviors and events. Heinrich strives to make it very accessible. Netwire follows a more algebraic path and drops the classic notion. The line between signals and events is blurred. It's a bit more difficult to understand, but is more expressive and concise. Also it's pretty much time-leak-free. The library is designed to be very elegant while preserving non-FRP performance to a high degree. (To be fair, I'm the author of Netwire.) =) 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.

I don't know that netwire is more difficult to understand. I'm
appreciating the network analogy and the generalisation of wires.
Thanks for pointing it out!
Cheers,
Darren
On Tue, Sep 18, 2012 at 7:35 PM, Ertugrul Söylemez
Christopher Howard
wrote: Which FRP framework (i.e., Haskell package) is the best one to play with for someone still trying to grasp the basics of FRP?
In terms of tutorials and related blog posts I currently recommend reactive-banana. In terms of power and elegance I recommend Netwire.
The main difference is that reactive-banana is both simple and easy. It implements classic FRP with the usual notion of behaviors and events. Heinrich strives to make it very accessible.
Netwire follows a more algebraic path and drops the classic notion. The line between signals and events is blurred. It's a bit more difficult to understand, but is more expressive and concise. Also it's pretty much time-leak-free. The library is designed to be very elegant while preserving non-FRP performance to a high degree.
(To be fair, I'm the author of Netwire.) =)
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.
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

A 19/09/2012, às 03:35, Ertugrul Söylemez escreveu:
Netwire follows a more algebraic path and drops the classic notion. The line between signals and events is blurred. It's a bit more difficult to understand, but is more expressive and concise. Also it's pretty much time-leak-free. The library is designed to be very elegant while preserving non-FRP performance to a high degree.
(To be fair, I'm the author of Netwire.) =)
Having recently looked a bit on Yampa, what are the main differences between Yampa and Netwire ? best, Miguel Negrão

Miguel Negrao
Netwire follows a more algebraic path and drops the classic notion. The line between signals and events is blurred. It's a bit more difficult to understand, but is more expressive and concise. Also it's pretty much time-leak-free. The library is designed to be very elegant while preserving non-FRP performance to a high degree.
(To be fair, I'm the author of Netwire.) =)
Having recently looked a bit on Yampa, what are the main differences between Yampa and Netwire ?
The way events are handled. Yampa uses the classic automaton category with an additional time delta argument for its signal function representation (Yampa's type is more complicated, but isomorphic to this one): type Time = Double newtype SF a b = SF (Time -> a -> (b, SF a b)) Netwire generalizes this category to a class of wire categories (simplified and without associated types): newtype Wire e a b = Wire (Time -> a -> (Either e b, Wire e a b)) SF is an arrow and an applicative functor (although Yampa does not provide an Applicative instance). 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. 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.

Ertugrul Söylemez 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. 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? 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? Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

Heinrich Apfelmus
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.

Move to haskell-cafe you two.
(j/k -- I think the discussion is great here. If you also cc'ed -cafe, your
points would get higher visibility.)
-- Kim-Ee
On Fri, Sep 21, 2012 at 8:53 AM, Ertugrul Söylemez
Heinrich Apfelmus
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.
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Thanks for the explanation! So, netwire is essentially based on the abstraction type Stoplight red green = Behavior' (Either red green) of time-varying values that can either be a "normal" value of type green , or an "exceptional" ("inhibiting") value of type red . (I have dropped the input dependence, so that arrow composition becomes normal function application.) Here, Behavior' denotes a type of continues time-varying values, with the small twist that it can "detect some flanks", namely when the value jumps from Right to Left . In this way, the flank can be interpreted as an Event in the reactive-banana sense. The applicative instance is the composition of the applicative instances for Behavior' and Either instance Monoid red => Applicative (Stoplight red) where pure x = pure (Right x) f <*> x = (<$)> <$> f <*> x if we assume that Either has the following applicative instance instance Monoid red => Applicative (Either red) where pure = Right (Right x) <*> (Right y) = Right (x y) (Left x) <*> (Right y) = Left x (Right x) <*> (Left y) = Left y (Left x) <*> (Left y) = Left (x <*> y) What I particularly like about this approach is the following: I like to think of Behavior as continuous functions, not necessarily because they are really continuous, but because this means that the semantics of Behavior is independent of any "update frequency", which is key point in simplifying code with FRP. Unfortunately, this means that we cannot "detect flanks", because a continuously changing Behavior simply does not change in discrete steps. The Stoplight type solves this problem by introducing an explicit "this is a flank" value, i.e. by using the fact that the only way an Either type can change is in discrete steps. Due to parametricity, I can't put this information into the general Behavior type, but the special case Behavior (Either red green) can make use of this. On the other hand, while the combination of Behaviors and Events allows for some slick switching combinators, I'm not entirely sure whether the mixture of two unrelated concepts (continuous functions vs singular occurrences) is too much of a conceptual burden. Best regards, Heinrich Apfelmus Ertugrul Söylemez wrote:
Heinrich Apfelmus 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.

Heinrich Apfelmus
So, netwire is essentially based on the abstraction
type Stoplight red green = Behavior' (Either red green)
of time-varying values that can either be a "normal" value of type green , or an "exceptional" ("inhibiting") value of type red . (I have dropped the input dependence, so that arrow composition becomes normal function application.)
Here, Behavior' denotes a type of continues time-varying values, with the small twist that it can "detect some flanks", namely when the value jumps from Right to Left . In this way, the flank can be interpreted as an Event in the reactive-banana sense.
This is not really how it works. Inhibition causes the rest of the wire to inhibit, in other words: _ . empty = empty
The applicative instance is the composition of the applicative instances for Behavior' and Either
instance Monoid red => Applicative (Stoplight red) where pure x = pure (Right x) f <*> x = (<$)> <$> f <*> x
if we assume that Either has the following applicative instance
instance Monoid red => Applicative (Either red) where pure = Right (Right x) <*> (Right y) = Right (x y) (Left x) <*> (Right y) = Left x (Right x) <*> (Left y) = Left y (Left x) <*> (Left y) = Left (x <*> y)
In particular the above means that with (<*>) when the function wire inhibits, the application itself inhibits: empty <*> _ = empty The Monoid constraint is on the Alternative class instead, where selection happens. The reason why it has to be a monoid is for the Alternative laws to hold.
What I particularly like about this approach is the following: I like to think of Behavior as continuous functions, not necessarily because they are really continuous, but because this means that the semantics of Behavior is independent of any "update frequency", which is key point in simplifying code with FRP. Unfortunately, this means that we cannot "detect flanks", because a continuously changing Behavior simply does not change in discrete steps.
The Stoplight type solves this problem by introducing an explicit "this is a flank" value, i.e. by using the fact that the only way an Either type can change is in discrete steps. Due to parametricity, I can't put this information into the general Behavior type, but the special case Behavior (Either red green) can make use of this.
Netwire doesn't do everything to hide the discrete nature of stepping, but this isn't really a conceptual choice, but rather the simple fact that I provide predefined wires that expose this (for example in Control.Wire.Prefab.Accum). If you use only time-continuous wires, then the perception is the same as in a continuous model, probably even more so as handling events becomes simple algebra instead of explicit switching: "yes" . holdFor 1 (periodically 2) <|> "no" The reason is that Netwire is developed with flexibility in mind. In fact the original motivation was to enable development of reactive networking applications, where time or continuity isn't much of a concern. I wanted to be able to express such systems declaratively without giving up the ability to reason easily about how it will behave at runtime. There is an experimental web framework based on Netwire 1 called Webwire, and I'm planning to revive it, because FRP makes developing web applications (server side!) really convenient, especially when you have to handle forms or other interactive things.
On the other hand, while the combination of Behaviors and Events allows for some slick switching combinators, I'm not entirely sure whether the mixture of two unrelated concepts (continuous functions vs singular occurrences) is too much of a conceptual burden.
The wire concept is at least equivalent to the classic events/behaviors split, because you can translate both behaviors and events to wires. You will find that most event signal functions from Yampa are also present in Netwire, except that they are potentially inhibiting wires instead of event signals. 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.

A 20/09/2012, às 03:56, Ertugrul Söylemez escreveu:
Miguel Negrao
wrote: Netwire follows a more algebraic path and drops the classic notion. The line between signals and events is blurred. It's a bit more difficult to understand, but is more expressive and concise. Also it's pretty much time-leak-free. The library is designed to be very elegant while preserving non-FRP performance to a high degree.
(To be fair, I'm the author of Netwire.) =)
Having recently looked a bit on Yampa, what are the main differences between Yampa and Netwire ?
The way events are handled. Yampa uses the classic automaton category with an additional time delta argument for its signal function [...]
Thanks for the explanation. I was wondering, how would one translate this Yampa code into reactive-banana: fallingBall :: Pos -> Vel -> SF () (Pos, Vel) fallingBall y0 v0 = proc () -> do v <- (v0 +) ˆ<< integral -< -9.81 y <- (y0 +) ˆ<< integral -< v returnA -< (y, v) fallingBall’ :: Pos -> Vel -> SF () ((Pos,Vel), Event (Pos,Vel)) fallingBall’ y0 v0 = proc () -> do yv@(y, _) <- fallingBall y0 v0 -< () hit <- edge -< y <= 0 returnA -< (yv, hit ‘tag‘ yv) bouncingBall :: Pos -> SF () (Pos, Vel) bouncingBall y0 = bbAux y0 0.0 where bbAux y0 v0 = switch (fallingBall’ y0 v0) $ \(y,v) -> bbAux y (-v) Would it be possible to do this without dynamic event switching ? What about with the new event switching in v0.7 ? Also, is it possible (and is it easy ?) to do looping such as it is done in Yampa using the the loop arrow in reactive-banana/classic FRP ? best, Miguel Negrão

Miguel Negrao wrote:
Thanks for the explanation. I was wondering, how would one translate this Yampa code into reactive-banana:
fallingBall :: Pos -> Vel -> SF () (Pos, Vel) fallingBall y0 v0 = proc () -> do v <- (v0 +) ˆ<< integral -< -9.81 y <- (y0 +) ˆ<< integral -< v returnA -< (y, v)
fallingBall’ :: Pos -> Vel -> SF () ((Pos,Vel), Event (Pos,Vel)) fallingBall’ y0 v0 = proc () -> do yv@(y, _) <- fallingBall y0 v0 -< () hit <- edge -< y <= 0 returnA -< (yv, hit ‘tag‘ yv)
bouncingBall :: Pos -> SF () (Pos, Vel) bouncingBall y0 = bbAux y0 0.0 where bbAux y0 v0 = switch (fallingBall’ y0 v0) $ \(y,v) -> bbAux y (-v)
Would it be possible to do this without dynamic event switching ? What about with the new event switching in v0.7 ? Also, is it possible (and is it easy ?) to do looping such as it is done in Yampa using the the loop arrow in reactive-banana/classic FRP ?
The Animation.hs example may help http://www.haskell.org/haskellwiki/Reactive-banana/Examples#animation Essentially, the main difference is that reactive-banana doesn't have functions like integral or edge because they depend on an implicit time step. In reactive-banana, you have to manage time yourself, for instance by making a timer event from a wxTimer. To calculate velocity and position, you can you use accumE on the timer event. To do collision detection, you can check whether the ball will move below the floor at each time step. Dynamic event switching is not needed here. For a more complex example, have a look at Andreas Bernstein's paddle ball game https://github.com/bernstein/breakout Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

A 01/10/2012, às 09:49, Heinrich Apfelmus escreveu:
Miguel Negrao wrote:
Thanks for the explanation. I was wondering, how would one translate this Yampa code into reactive-banana: fallingBall :: Pos -> Vel -> SF () (Pos, Vel) fallingBall y0 v0 = proc () -> do v <- (v0 +) ˆ<< integral -< -9.81 y <- (y0 +) ˆ<< integral -< v returnA -< (y, v) fallingBall’ :: Pos -> Vel -> SF () ((Pos,Vel), Event (Pos,Vel)) fallingBall’ y0 v0 = proc () -> do yv@(y, _) <- fallingBall y0 v0 -< () hit <- edge -< y <= 0 returnA -< (yv, hit ‘tag‘ yv) bouncingBall :: Pos -> SF () (Pos, Vel) bouncingBall y0 = bbAux y0 0.0 where bbAux y0 v0 = switch (fallingBall’ y0 v0) $ \(y,v) -> bbAux y (-v) Would it be possible to do this without dynamic event switching ? What about with the new event switching in v0.7 ? Also, is it possible (and is it easy ?) to do looping such as it is done in Yampa using the the loop arrow in reactive-banana/classic FRP ?
The Animation.hs example may help
http://www.haskell.org/haskellwiki/Reactive-banana/Examples#animation
Essentially, the main difference is that reactive-banana doesn't have functions like integral or edge because they depend on an implicit time step. In reactive-banana, you have to manage time yourself, for instance by making a timer event from a wxTimer.
To calculate velocity and position, you can you use accumE on the timer event. To do collision detection, you can check whether the ball will move below the floor at each time step. Dynamic event switching is not needed here.
For a more complex example, have a look at Andreas Bernstein's paddle ball game
Yes, looking at the internals of Yampa I had seen that they have internal time management, but my question was more specifically if there was a way to have a function like bbAux which recursively switches into itself. Would it be possible with the new dynamic switching ? I find that way of expressing the discontinuous changes quite elegant. Even more elegant seems to be the instantaneous impulses (modeling of distributions or dirac deltas) although I couldn’t find any functioning code that implements it [1]. The breakout game code you mentioned is an excelent example of FRP in use, and best of all, it actually compiles !! I’ve lost count of the FRP programs I’ve tried to compile without success (mostly yampa or YFRP related). It was very instructional to see how it implements this kind of game logic in reactiva-banana. It’s also a good example of recursive definitions in reactiva-banana: the ball velocity depends on the ball position and vice-versa. Thank you for the link and the explanations, Miguel [1] http://haskell.cs.yale.edu/wp-content/uploads/2011/01/icfp03.pdf bouncing :: Position -> SF () (Position, Velocity) bouncing y0 = proc () -> do rec y <- (y0 +) ^<< integralG -< yd_ni hit <- edge -< y <= 0 yd <- integralG -< -9.81 + impulse (hit ‘tag‘ (-2*leftLimit yd)) yd_ni <- assertNoImpulseG -< yd returnA -< (y, yd)

Dear Miguel, sorry for the late reply, I have been traveling in the last weeks. Miguel Negrao wrote:
Heinrich Apfelmus escreveu:
Miguel Negrao wrote:
Thanks for the explanation. I was wondering, how would one translate this Yampa code into reactive-banana:
fallingBall :: Pos -> Vel -> SF () (Pos, Vel) fallingBall y0 v0 = proc () -> do v <- (v0 +) ˆ<< integral -< -9.81 y <- (y0 +) ˆ<< integral -< v returnA -< (y, v) fallingBall’ :: Pos -> Vel -> SF () ((Pos,Vel), Event (Pos,Vel)) fallingBall’ y0 v0 = proc () -> do yv@(y, _) <- fallingBall y0 v0 -< () hit <- edge -< y <= 0 returnA -< (yv, hit ‘tag‘ yv) bouncingBall :: Pos -> SF () (Pos, Vel) bouncingBall y0 = bbAux y0 0.0 where bbAux y0 v0 = switch (fallingBall’ y0 v0) $ \(y,v) -> bbAux y (-v)
[...]
Yes, looking at the internals of Yampa I had seen that they have internal time management, but my question was more specifically if there was a way to have a function like bbAux which recursively switches into itself. Would it be possible with the new dynamic switching ? I find that way of expressing the discontinuous changes quite elegant.
Yes, it's possible, but it will feel alien. There are two reasons for that: 1. switchB is meant to change the behavior whenever the event occurs, not just on the first occurrence. The bbAux function is recursive precisely because only the first occurrence causes a switch in Yampa. Maybe reactive-banana should adopt this approach as well, but I'm undecided. 2. In Yampa, fallingBall is a signal function, which means that it has no fixed starting time. In contrast, behaviors in reactive-banana usually have a fixed starting time so that they can accumulate state. Of course, reactive-banana also has behaviors that have a variable starting time, but they are a separate type. In other words, you have to model it as fallingBall :: Pos -> Vel -> AnyMoment Behavior Pos So, modeling this using dynamic event switching is possible, but these two points mean that it's probably much easier to use a recursive event/behavior combination like in the Animation.hs example or Andreas Bernstein's breakout code. As you can see, dynamic event switching is still uncharted territory, there are a lot of design patterns to discover.
Even more elegant seems to be the instantaneous impulses (modeling of distributions or dirac deltas) although I couldn’t find any functioning code that implements it [1].
Well, I do not think that mixing continuous signals and discrete events in this way is a good idea. Having two separate types gives you more information to reason with: Event can only be discrete, Behavior can only be continuous. I would like to stress again that the design space for FRP is huge, which is good. But the ultimate goal is to simplify a certain class of code, namely GUIs, audio and games, and I feel that few, if any FRP approaches have been tested enough against these "hard" targets. I mean, if I use FRP to implement a game and the code is just as messy as the imperative version, then I may wonder why I am doing this at all. So far, I only know of Conal who makes a convincing argument for his style Conal Elliott. Declarative Event-Oriented Programming. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.31.1064 (Interestingly, he doesn't really use dynamic event switching in this instance.) Simplicity also hinges on a lot of details, see for instance my discussion on GUI elements http://apfelmus.nfshost.com/blog/2012/03/29-frp-three-principles-bidirection... Conal's decision to allow recursion between Event and Behavior is one of these very important details. Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

On Wed, Sep 19, 2012 at 4:20 AM, Christopher Howard wrote:
Which FRP framework (i.e., Haskell package) is the best one to play with for someone still trying to grasp the basics of FRP?
I think of FRP as a Dream that's still being worked on. So to get at the basics of the Dream I do a lot of gedankenexperiment, sketching on pen and paper, and code fragments. I read Conal Elliott's blog to gather the results of his investigations of that Dream. But I understand that you're a practical kind of guy and prefers to push and poke to learn stuff, and I think all the projects mentioned in this thread are excellent starting points. -- Kim-Ee
participants (6)
-
Christopher Howard
-
Darren Grant
-
Ertugrul Söylemez
-
Heinrich Apfelmus
-
Kim-Ee Yeoh
-
Miguel Negrao