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.
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.)
Related
I'm using Netwire to write a program that is driven by events from the network. I guess there are three questions here:
What makes Control.Wire.Unsafe.Event unsafe? Like the title says, what invariants do I need to maintain to use it safely?
I decided I need something like this: mapMaybeE :: Monad m => (a -> Maybe b) -> Wire s e m (Event a) (Event b). The context is I have messages coming in from the network and I want to respond to only some of them. This is what I wrote:
mapMaybeE :: Monad m => (a -> Maybe b) -> Wire s e m (Event a) (Event b)
mapMaybeE f = arr go . arr (fmap f)
where go WU.NoEvent = WU.NoEvent
go (WU.Event Nothing) = WU.NoEvent
go (WU.Event (Just a)) = WU.Event a
Is that "legal"? Or am I supposed to inhibit if there's no event?
Does Netwire make sense for this sort of problem? All the examples I've seen are of games that loop continuously. Here, I only want to step the wires when there's something to be done. Mostly, that will be network events, but I might also want to do things on a timer. E.g. an event comes in, then five seconds later the program does something. It shouldn't have to loop continuously until the time in the session is five seconds greater than when the event came in.
For a lot of these answers, "correct" or "legal" depends on what you want your application to do. "Idiomatic" might be a more interesting question, but as the library author has passed away, it's difficult to answer these questions definitively. The following therefore only represents my experience and may not be correct:
The "unsafe" part of Control.Wire.Unsafe.Event is the idea that you will be working with discrete instances in time, and you may not necessarily preserve the continuous time semantics that your program expects. In particular, there's no difference (from a type perspective) between an event happening in a simulation with a time state (the s in Wire s e m a b) that's represented as an Integer vs a Float, so you have to be careful to make sure that what you're doing makes sense for your application. The included general purpose combinators don't have that risk in the sense that they work with any sensible definition of "time". From the documentation of data Event:
Denotes a stream of values, each together with time of occurrence. Since Event is commonly used for functional reactive programming it does not define most of the usual instances to protect continuous time and discrete event occurrence semantics.
From the README:
If you are a framework developer you can import the Control.Wire.Unsafe.Event module to implement your own events. A game engine may include events for key presses or certain things happening in the scene. However, as an application developer you should view this type as being opaque. This is necessary in order to protect continuous time semantics. You cannot access event values directly.
There's certainly nothing "illegal" about doing it that way. When you inhibit is totally dependent on your application. The key difference being that if you compose wires that inhibit, the inhibition "bubbles up" to the first wire that handles it (such as with an Alternative: (<|>)). Producing NoEvent is fine if there's no event. :) The behavior you're looking for might be better modeled using the existing combinators dropWhileE and fmap from Control.Wire.Event though:
mapMaybeE :: Monad m => (a -> Maybe b) -> Wire s e m (Event a) (Event b)
mapMaybeE = arr (fmap fromJust) . dropWhileE isNothing . arr (fmap f)
Yes, netwire makes sense for any problem where you have to simulate the state of a system that has time-dependent semantics. To expand,
It shouldn't have to loop continuously until the time in the session is five seconds greater than when the event came in.
Something is going to need to keep track of this timer, so you're not going to get around having a loop in one capacity or another. (Maybe you can have the operating system do it via a call to sleep or something, but internally there's still some loop somewhere...) netwire lets you model the behavior of your system explicitly and respond to these kinds of events: both network events and timer events. Because Haskell is lazy, if you compose wires such that "really complicated wire" depends on a timer, the result from "really complicated wire" will not be evaluated until the timer expires (see after.)
I'm rather new to Elm, and I'm deeply attracted by the way Elm dealing with GUI. But after some deep thought, I find it's hard to efficiently update just one element of a list or finger tree (Just like finger tree in Haskell, if it already exists in Elm's library) which is under a Signal and the size of it also varies against time.
Specifically, to express a dynamic finger tree, we have to write
Signal [ {-the finger tree's element type-} ]
But if we want to update just one element of the finger tree efficiently, we have to write
Signal [ Signal {-the core data type-} ]
But in Elm the Signal is not a Monad, then how to flatten the two layer Signals into one layer?
Comment 1: I don't know in detail how Elm behaves in this situation. Reprocessing the whole finger tree is just my guess.
Comment 2: For example, suppose we have a signal value, marked as s, of type Signal (fingerTree Int), and the following function, marked as f, whose input is s is, for example, lift (fmap (+1)) whose type is Signal (fingerTree Int) -> Signal (fingerTree Int). And if s has just one element changed, function f has to re-do the (+1) operation for every element of s. Obviously, it's a waste of time and I'm not sure if Elm is intelligent enough to detect the immutability.
TL;DR: Implement your logic/data processing as pure functions and lift them to transform signals.
The trick is to write a function processList : [elementType] -> [elementType] that provides the logic you want (check if the third element is a wibbler and change it to a wobbler or whatever you wanted to do), and then use the lift function which has type
lift : (a -> b) -> Signal a -> Signal b
like lift processList mySignalThatProducesLists to edit the data produced by mySignalThatProducesLists using processList.
The main idea here is that you encode the logic and data processing as pure functions and then use them as Signal transformers using lift. It's like Elm automatically reapplies the function every time the data in the source signal is updated.
If you're used to programming in Haskell, you can think of Signal a as a newtype wrapper around Time -> a for some opaque Time type, and lift as fmap. You don't need to have a monad to edit data.
There are also lift2 andlift3functions for lifting functions with more than one argument, so again to use a Haskell analogy, you essentially have the capabilities of an Applicative functor there.
The reason they don't have a Monad is that that imposes implementation details that reduce efficiency. It may be that there's some equivalent of ArrowApply in there that I haven't spotted (that would give you an expressibility eqiuivelent of Monad), but I'm not sure there is.
In a previous SO question (Is it possible?: Behavior t [Behavior t a] -> Behavior t [a]) we were analyzing the existence of a Behavior join (to use reactive-banana terms).
Behavior t (Behavior t a) -> Behavior t a
Implemented in the semantic model as follows
type Behavior t a = t -> a
behaviorNow :: Behavior t (Behavior t a) -> Behavior t a
behaviorNow f t = f t t
While implementing this directly would be unfortunate since we could produce a Behavior Monad using const and behaviorNow, if and how does behaviorNow violate the semantics of FRP?
I'd love to hear answers using the terminology of any other FRP system along with comparisons if meaningful.
In a poll based FRP system, any behavior has a meaningful join
the sample of join bb is the sample of the b obtained by sampling bb
In push based FRP, any behavior that is a step function composed with other step functions has a meaningful >>= and join. Pushing values through >>= can be described in imperative terms:
when the argument of the bind changes, evaluate the bind and
change the current step function to the returned step function
change the value to the value of the current step function
when the value of the current step function changes, change the value
Providing a Monad instance may be slightly undesirable because it is likely to be chosen by preference by library users, even if it is less efficient. For example, the code in this unrelated answer performs more work when a computation was built with >>= than if it had been equivalently built with <*>.
Conal Elliott described in declarative terms a join for simultaneously pushing and polling values for behaviors built from step functions:
-- Reactive is a behavior that can only be a step function
data Reactive a = a `Stepper` Event a
newtype Event a = Ev (Future (Reactive a))
join :: Reactive (Reactive a) -> Reactive a
join ((a `Stepper` Ev ur) `Stepper` Ev urr ) =
((`switcher` Ev urr ) <$> ur) _+_ (join <$> urr )
switcher :: Reactive a -> Event (Reactive a) -> Reactive a
r `switcher` er = join (r `Stepper` er)
where Future is the type for a value we haven't seen yet, _+_ is the first of the two Future possibilities to occur, and <$> is infix fmap on Futures. [1]
If we don't provide any other means of creating behaviors than
the constant function (which is trivially a step function)
a "stepper" that remembers the most recent value of an event
application of various combinators of behaviors where the combinators themselves aren't time-varying
then every behavior is a step function and we can use this or a similar Monad instance for behaviors.
Difficulties only arise when we want to have behaviors that are continuous or are a function of some time other than when an event occurred. Consider if we had the following
time :: Behavior t t
which is the behavior that tracks the current time. A Monad instance for polling the system would still be the same, but we can no longer push changes through the system reliably. What happens when we make something as simple as time >>= \x -> if am x then return 0 else return 1 (where am t is true for times in the morning)? Neither our definition of >>= above nor Elliot's join can admit the optimization of knowing when the time changes; it changes continuously. The best we could do to >>= is something like:
if we know that the argument to the bind is step valued then
when the argument of the bind changes, evaluate the bind and
change the current step function to the returned step function
change the value to the value of the current step function
when the value of the current step function changes, change the value
otherwise
return an abstract syntax tree for this >>=
For the join form, we would be reduced to doing something similar, and simply record the AST in the instance that the outer behavior in a join isn't a step function.
Additionally, anything built using this as an input could change at noon and midnight, whether or not any other event was raised. It would taint everything from that point on with the inability to reliably push events.
From an implementation point of view, our best option would seem to be to continuously poll time, and replace anywhere it was used with a stepper built from the polling events. This wouldn't update values between events, so now users of our library can't reliably poll values.
Our best choice for an implementation would be to keep an abstract syntax tree of what happened with arbitrary behaviors like these and provide no means to generate events from behaviors. Then behaviors can be polled, but no updates will ever be pushed for them. In that case, we might as well leave it out of the library, and let the user pass around ASTs (which they can get for Free), and let the user evaluate the entire AST every time it's polled. We can't optimize it any more for the library user, since any value like this can change continuously.
There is one final option, but it involves introducing quite a bit of complexity. Introduce the notion of predictability for properties of continuously varying values and computations of continuously varying values. This would allow us to provide a Monad interface for a larger subset of time-varying behaviors, but not for all of them. This complexity is already desirable in other parts of programs, but I don't know of any libraries outside symbolic math which attempt to address this.
(Author here.)
First note, that the behaviorNow function is the monadic join.
In reactive-banana-0.7, Behavior t is not a monad beause that would have serious consequences for efficiency.
The first and most important problem is that behaviors can also be stateful. In conjunction with join, this would lead to time leaks. The main indication of problems is that the starting time t of the inner Behavior t is the same as the outer one. For instance, consider the program
e :: Event t Int
b :: Int -> Behavior t Int
b x = accumB 0 $ (x+) <$ e
bb :: Behavior t (Behavior t Int)
bb = stepper (pure 0) $ b <$> e
The behavior join bb would need to keep track of the whole history of the event e in order to perform the accumulation in the definition of b. In other words, the event e could never be garbage collected -- a time leak.
A second problem is that internally, the implementation of Behavior t also includes an event that keeps track of when the behavior changes. However, a liberal use of the join combinator, for instance as implied by do notation, would lead to rather convoluted calculations to determine whether the behavior has changed or not. This is contrary to the reason for keeping track in the first place: efficiency by avoiding expensive calculations.
The Reactive.Banana.Switch module offers various combinators that are cousins of the join function, but avoid the first problem with cleverly chosen types. In particular:
The switchB function is the most direct analogue of join.
The AnyMoment Identity type is similar to the Behavior type, but without state and without keeping track of changes. Consequently, it has a monad instance.
I am creating a system that needs to store all the functions and parameters a user has run in a database. No records are ever deleted, but I need to be able to recreate the minimal function sequence and parameter set for deterministic regeneration.
The users interaction is very minimal, they are not programming - input interaction is handled in C++ is passed through the FFI as data to accumulate into lists and callback to process the current buffer of data. The function triggers a series of decisions on how to wire a processing graph of sets of data within the database, and functions they are input to. The graph is acyclic. This graph is initially run and values are visualized for the user. Later portions of the graph will be recombined to generate new graphs.
Haskell internal construction of these graphs is created from analysis of data in the database and simple random choices amongst combinations. I'd like to be able to just store a seed of a random generator, the module and parameter id to which it applies.
I think this may be best framed as storing the functions of a EDSL in a database, where only the highlevel interaction is stored but is fully deterministic.
I am not interested in storing the values, but rather the function graph of the action.
Each table refers to different function. Each record has a date and a task ID to group all the functions of specific actions to gether. The parameters reference a Table ID and Record ID. If a composed function is internally doing something like generating a random number, the seed for that number should be automatically stored.
I am using GHC stage 1 with no GHCI and Persistent SQlite.
I am still new to Haskell and am looking to find out what approach and packages would be appropriate for tackling this problem in a functional manner.
If you want to do this for source-level functions, such as:
myFoo x y = x + y
you are pretty much out of luck, unless you want to go hacking around in the compiler. However, you could define your own notion of function that does support this, with some suitable annotations. Let's call this notion a UserAction a, where a is the return type of the action. In order to compose computations in UserAction, it should be a Monad. Not thinking too awfully hard, my first impression would be to use this stack of monad transformers:
type UserAction = WriterT [LogEntry] (ReaderT FuncIdentifier IO)
The WriterT [LogEntry] component says that a UserAction, when run, produces a sequence of LogEntrys [1], which contain the information you want to write to the database; something like:
data LogEntry = Call FuncIdentifier FuncIdentifier
It's okay to put off storing the random seed, task identifier, etc. for now -- that can be incorporated into this design by adding information to LogEntry.
The ReaderT FuncIdentifier component says that a UserAction depends on a FuncIdentifier; namely, the identifier of the function that is calling it.
FuncIdentifier could be implemented by something as simple as
type FuncIdentifier = String
or you use something with more structure, if you like.
The IO component says that UserActions can do arbitrary input and output to files, the console, spawn threads, the whole lot. If your actions don't need this, don't use it (use Identity instead). But since you mentioned generating random numbers, I figured you did not have pure computations in mind[2].
Then you would annotate each action you want to record logs for with a function like this:
userAction :: FuncIdentifier -> UserAction a -> UserAction a
which would be used like so:
randRange :: (Integer, Integer) -> UserAction Integer
randRange (low,hi) = userAction "randRange" $ do
-- implementation
userAction would record the call and set up its callees to record their calls; e.g. something like:
userAction func action = do
caller <- ask
-- record the current call
tell [Call caller func]
-- Call the body of this action, passing the current identifier as its caller.
local (const func) action
From the top level, run the desired action and after it has finished, collect up all the LogEntrys and write them to the database.
If you need the calls to be written in real time as the code is executing, a different UserAction monad would be needed; but you could still present the same interface.
This approach uses some intermediate Haskell concepts such as monad transformers. I suggest going on IRC to irc.freenode.net #haskell channel to ask for guidance on filling out the details of this implementation sketch. They are a kind bunch and will happily help you learn :-).
[1] In practice you will not want to use [LogEntry] but rather DList LogEntry for performance. But the change is easy, and I suggest you go with [LogEntry] until you get more comfortable with Haskell, then switch over to DList.
[2] Random number generation can be done purely, but it takes further brain-rewiring which this sketch already has plenty of, so I suggest just treating it as an IO effect for the purpose of getting going.
I'm working on a program using reactive-banana, and I'm wondering how to structure my types with the basic FRP building blocks.
For instance, here's a simplified example from my real program: say my system is composed primarily of widgets — in my program, pieces of text that vary over time.
I could have
newtype Widget = Widget { widgetText :: Behavior String }
but I could also have
newtype Widget = Widget { widgetText :: String }
and use Behavior Widget when I want to talk about time-varying behaviour. This seems to make things "simpler", and means I can use Behavior operations more directly, rather than having to unpack and repack Widgets to do it.
On the other hand, the former seems to avoid duplication in the code that actually defines widgets, since almost all of the widgets vary over time, and I find myself defining even the few that don't with Behavior, since it lets me combine them with the others in a more consistent manner.
As another example, with both representations, it makes sense to have a Monoid instance (and I want to have one in my program), but the implementation for the latter seems more natural (since it's just a trivial lifting of the list monoid to the newtype).
(My actual program uses Discrete rather than Behavior, but I don't think that's relevant.)
Similarly, should I use Behavior (Coord,Coord) or (Behavior Coord, Behavior Coord) to represent a 2D point? In this case, the former seems like the obvious choice; but when it's a five-element record representing something like an entity in a game, the choice seems less clear.
In essence, all these problems reduce down to:
When using FRP, at what layer should I apply the Behavior type?
(The same question applies to Event too, although to a lesser degree.)
The rules I use when developing FRP applications, are:
Isolate the "thing that changes" as much as possible.
Group "things that change simultaneously" into one Behavior/Event.
The reason for (1) is that it becomes easier to create and compose abstract operations if the data types that you use are as primitive as possible.
The reason for this is that instances such as Monoid can be reused for raw types, as you described.
Note that you can use Lenses to easily modify the "contents" of a datatype as if they were raw values, so that extra "wrapping/unwrapping" isn't a problem, mostly. (See this recent tutorial for an introduction to this particular Lens implementation; there are others)
The reason for (2) is that it just removes unnecessary overhead. If two things change simultaneously, they "have the same behavior", so they should be modeled as such.
Ergo/tl;dr: You should use newtype Widget = Widget { widgetText :: Behavior String } because of (1), and you should use Behavior (Coord, Coord) because of (2) (since both coordinates usually change simultaneously).
I agree with dflemstr's advice to
Isolate the "thing that changes" as much as possible.
Group "things that change simultaneously" into one Behavior/Event.
and would like to offer additional reasons for these rules of thumb.
The question boils down to the following: you want to represent a pair (tuple) of values that change in time and the question is whether to use
a. (Behavior x, Behavior y) - a pair of behaviors
b. Behavior (x,y) - a behavior of pairs
Reasons for preferring one over the other are
a over b.
In a push-driven implementation, the change of a behavior will trigger a recalculation of all behaviors that depend on it.
Now, consider a behaviors whose value depends only on the first component x of the pair. In variant a, a change of the second component y will not recompute the behavior. But in variant b, the behavior will be recalculated, even while its value does not depend on the second component at all. In other words, it's a question of fine-grained vs coarse-grained dependencies.
This is an argument for advice 1. Of course, this is not of much importance when both behaviors tend to change simultaneously, which yields advice 2.
Of course, the library should offer a way to offer fine-grained dependencies even for variant b. As of reactive-banana version 0.4.3, this is not possible, but don't worry about that for now, my push-driven implementation is going to mature in future versions.
b over a.
Seeing that reactive-banana version 0.4.3 does not offer dynamic event switching yet, there are certain programs that you can only write if you put all components in a single behavior. The canoncial example would be a program that features variable number of counters, i.e. an extension of the TwoCounter.hs example. You have to represent it as a time-changing list of values
counters :: Behavior [Int]
because there is no way to keep track of a dynamic collection of behaviors yet. That said, the next version of reactive-banana will include dynamic event switching.
Also, you can always convert from variant a to variant b without any trouble
uncurry (liftA2 (,)) :: (Behavior a, Behavior b) -> Behavior (a,b)