16

I have been researching FRP and found a bunch of different implementations. One model I have seen is one I will refer to as the 'Signal' representation. This essential combines Events and Behaviours into one entity.

Firstly, a Signal is an object thats value is a Behaviour. Secondly, a Signal has an Event 'stream' that can be seen and operated on as a standard data structure (you can use 'each', 'map' and 'filter' etc on the Signal to define how Events are reacted to). For example I can do this (where 'time' is a Signal representation of time):

time.each { t => print(t) } // every time there is a 'tick' the current time is printed
a = time * 5 //where 'a' would be a dynamic value always up to date with time

Is this representation of FRP correct or are there any problems? I quite like the way this works and also how simple it is to describe personally but I'm not sure its right.

seadowg
  • 4,033
  • 6
  • 32
  • 43

1 Answers1

14

Unfortunately, coalescing "event" and "behavior" into a single entity "signal" doesn't work so well.

Most signal-based FRP implementations that I know end up creating an additional "event"-like type along the lines of

type Event a = Signal (Maybe a)

So, the concept of events doesn't go away, and there is no real simplification. In fact, I would argue that the signal type is a semantic complification. Signals are only popular because they are easier to implement.

The main argument against signals is that they cannot represent continuous time behaviors, because they have to cater to the discrete events. In Conal Elliott's original vision, behaviors were simple continuous functions of time

type Behavior a = Time -> a
-- = function that takes the current time as parameter and returns
--   the corresponding value of type  a

In contrast, signals always are always discretized and usually associated with a fixed time step. (It is possible to implement both events and behaviors on top of a variable time step signal, but it's not a good abstraction by itself.) Compare this to an event stream

type Event a = [(Time,a)]
-- list of pairs of the form (current time, corresponding event value)

where the individual events don't necessarily occur in regularly spaced time intervals.

The argument for the distinction between behaviors and events is that their API is quite different. The main point is that they have different product types:

(Behavior a , Behavior b) = Behavior (a,b)
(Event a    , Event b   ) = Event (a :+: b)

In words: a pair of behaviors is the same as a behavior of pairs, but a pair of events is the same as an event from either component/channel. Another point is that there are two operations

(<*>) :: Behavior (a -> b) -> Behavior a -> Behavior b
apply :: Behavior (a -> b) -> Event a    -> Event b

that have almost the same type, but quite different semantics. (The first updates the result when the first argument changes, while the second doesn't.)

To summarize: signals can be used for implementing FRP and are valuable for experimenting with new implementation techniques, but behaviors and events are a better abstraction for people who just want to use FRP.

(Full Disclosure: I have implemented an FRP library called reactive-banana in Haskell.)

Heinrich Apfelmus
  • 11,039
  • 1
  • 35
  • 66
  • So instead of having 'Time' as both the behaviour and the event it would be more correct to have 'Time' as a behaviour and something called 'Clock' as a completely separate Event stream? – seadowg Sep 17 '11 at 11:40
  • 1
    @Oetzi: Ah, I'm using Haskell notation. What I mean is that `Behavior a` is a function from time (`Time`) to some value of type `a`, i.e. it's simply a function that takes the current time as parameter and returns the corresponding value. In contrast, an event stream would be a list of event occurrences. There is no clean way to unify both into some notion of "signal", because a behavior can be something like `example_behavior(time) = 2*time`. – Heinrich Apfelmus Sep 17 '11 at 17:02
  • Yeah that makes sense. I'm implementing FRP for my dissertation and I think I might use pure Behaviours and Events and then implements Signals as well using them - just for the purpose of comparison. Thanks very much for the help! – seadowg Sep 18 '11 at 09:39
  • What do you think of Elm since it only has signals and not events/behaviours? – CMCDragonkai Nov 19 '14 at 12:37
  • @CMCDragonkai Elm's `Signal` type is a mixture of both `Event` and `Behavior`. I think it would be clearer to use distinct types, but I haven't used Elm enough to be able to pinpoint concrete examples of this. – Heinrich Apfelmus Nov 20 '14 at 13:44
  • @HeinrichApfelmus these slides (http://www.slideshare.net/SergeiWinitzki/temporal-logic-and-functional-reactive-programming) indicate that FRP signals, events, behaviours are just forms of temporal operators (from linear temporal logic) including G for globally (behaviour) and F for finally/eventually (events). I guess a signal would be something like G(Fa). – CMCDragonkai Dec 15 '14 at 11:38
  • Basically we have an infinite stream of events, in which each event may denote the value now, or the value later. – CMCDragonkai Dec 15 '14 at 11:48