-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Recoil is a FRP library, with a few differences to standard FRP.
to build it use the recoil-build repository it includes closure and recoil. It does this in order to allow the compile to be staged outside the recoil repository.
Recoil supports bidirectional behaviours built into the engine, I this allows the engine to control the flow of through the system better. I believe bidirectional behaviours allow for better abstraction of data types an a better way of thinking behaviours. The program is simply a view on data. A widget gets a behaviour and its job is either set or display that data. It provides a cleaner method than dealing with output and input events which seem to lead to state driven code even outside the widget.
Behaviours and events change inside a transaction. Nothing gets propagated until that transaction has completed.
Here is an example where this is useful.
C is behaviour that depends on A and B A and B are both updated on the server and sent to the client However if set either A or B first then C is temporarily in an inconsistent state resulting in errors, however if A and B are set in a transaction then neither propagates until both are done.
Every Behaviour has meta data that comes with errors and a ready state, not although events implement this it really doesn't make sense for events so they are always ready and have no errors, although the events themselves can be errors.
This means the system can handle the errors itself no more prefixing each function with if (!isGood())
, not ready and errors will be propagated automatically. You can do it yourself in the rare cases in which this is necessary using metaLiftB
.
In systems I have seen behaviours are events with states, in recoil events list of values that loose there state.
The reason they are a list of values is because of transactions, since an event may fire multiple times in a transaction.
This design leads no need for merge or map operations, the lift operation is enough. This greatly simplifies the code. Take for example merge, when you call merge you have no way of knowing what source the event came from, although they may be of different types so you end up having tag each event and place a in the map function afterwards.
C = frp.merge(tag("a",A), tag("b", B))
.map(function (e) {
if (e.tag === "a") ...
if (e.tag === "b") ...
})
however with a lift you can directly get the source of the event
C = frp.lift(function (a, b) {
a.forEach(...)
b.forEach(...)
});
Also you may note you can combine events and behaviours in lift, there is no need to use valueNow (unsafeMetaGet in recoil) which effectively breaks the dependency tree in the FRP engine.
Because of the bidirectionality of behaviours the is usually no need to send an event, you simply set value of the behaviour.
goog.event.listen(input, "change", function () {
frp.accessTrans(function () {
valueB.set(input.value)
, valueB)});
I have not needed events yet, so they are largely untested.
The system will error if you try to access a behaviour that you are not meant to for example:
A = frp.lift(function () {
return B.get();
})
this is invalid since you have not specified by as a provider the correct way is:
A = frp.lift(function (b) {
return B.get();
},B)
You can use either the b, or B.get() to get the value although using b seems functionally more clean in practice B.get() can work better for large function since you have to maintain the order of the parameters passed and scroll down to the bottom to find out what exactly B is.
FRP nodes are not calculated until they are attached, there is a recoil.frp.DomObserver
which will automatically attach detach the FRP based on the DOM's elements present in the document. You could implement this on the server side as well based on registration