I'd like to see a use case that would be clearer with this.
Inserting values in flapjax is normally very compositional with clear single assignments. E.g., x = DIV(time).
InsertDomB / insertValueB are the exception. Nesting them within code returns us to the inverted style we're trying to avoid, so I'm not sure we should endorse it. Furthermore, mixing together both an assignment and a composition might be very misleading. Generally, I only expect one side effect, not multiple as in insertDomB(DIV(insertValueB(time, 'x')). That was my reasoning originally, and it hasn't changed much since then.
> InsertDomB / insertValueB are the exception. Nesting them within code > returns us to the inverted style we're trying to avoid, so I'm not sure > we should endorse it.
Could you give more details on what an "inverted style" is? I don't quite get it.
> Again, perhaps there is a clear use case?
I was tinkering with Flapjax-based animation library, the goal being animating elements of existing web-pages. (No supplanting of default browser behavior, just adding some eye-candy via unobtrusive scripting).
(Basically, all I need is "reactive CSS properties" with proper units and a way to easily assign them to elements.)
Since I don't know what kind of API to settle on, I have tried to find prior work similar to the task at hand. I found "Composing reactive animations" by Conal Elliott, which describes an interface very much like I posted.
ATM, using the library is tedious:
> var elem = document.getElementById('foo'); > var a = anim(power(3.0))(-30.0, 0.0); > var posB = liftB(a, moveB(clicksE(elem), 25, 250)); > insertValueB(liftB(function(x) {return x + "em";}, posB), elem, 'style', 'left');
> var anim = function(f) { > return function(a,b) { > return function(x) { return a + (b - a) * f(x); } > } > } > // an example easing function > var power = function(y) { return function(x) { return Math.pow(x,y); } }
> // moveB :: EventStream a * [Behavior] Int * [Behavior] Int -> Behavior Float > // very much like what Arjun showed me months ago > function moveB(ev,step,duration) { > var timerB = timerB(step); > var startB = startsWith(snapshotE(ev,timeB), 0); > return liftB(function(a,b,d) {return Math.min((b - a)/d, 1.0);}, > startB, timeB, duration); > }
I tried to abstract some details out, by defining a bunch of functions analogous to:
> // one function for one animatable css property :) > var left = function(b, elem) { > insertValueB(b, elem, 'style', 'left'); > return elem; > }
(and fiddling with anim function), so they could be composed and abstracted out, just like in Fran. Then I noticed that insertValueB doesn't return anything, and an inquiry popped up.
>> InsertDomB / insertValueB are the exception. Nesting them within code
>> returns us to the inverted style we're trying to avoid, so I'm not sure
>> we should endorse it.
> Could you give more details on what an "inverted style" is? I don't
> quite get it.
This is really a pedantic red herring for the original question, but:
Some nestings of insertValueB follow our desired singly-assigned data flow style, and others don't. The following pseudocode example is in the spirit of Flapjax:
function makeWidget() {
var widget = DIV();
...
insertValueB(widget, time().apply_b(f, odds, evens));
return widget;
}
However, I can also see somebody also writing it as:
function insertOdds(w) {
insertValueE(w, time_e.filter_e(odds), 'style', 'x');
}
function insertEvens(w) {
insertValueE(w, time_e.filter_e(events), 'style', 'x');
The latter gets back to the style of code we're trying to avoid, where you can't just look at the assignment of some value and be guaranteed it's not getting reassigned elsewhere. When we say 'inverted', we mean that the data flow from source (time) to sink (widget.style.x) is opposite of the control-flow. This happens with callbacks: you need to check which functions might have called the callback, and which might have called that one, etc. In the first example, you have the full definition: f(time(), odds, evens). In the second example, you have to look around the entire program.
Whether or not insertValue returns the value inserted (or perhaps the object getting a value inserted), which is your concern, seems somewhat orthogonal to this issue of nesting calls to insertValue. Getting back to the email...
> I was tinkering with Flapjax-based animation library, the goal being animating
> elements of existing web-pages. (No supplanting of default browser behavior,
> just adding some eye-candy via unobtrusive scripting).
> (Basically, all I need is "reactive CSS properties" with proper units and a way
> to easily assign them to elements.)
> Since I don't know what kind of API to settle on, I have tried to find
> prior work
> similar to the task at hand. I found "Composing reactive animations" by Conal
> Elliott, which describes an interface very much like I posted.
> ATM, using the library is tedious:
An animation and widget library would make a lot of sense :) We've all been rolling our own individually so far it seems, and I don't know if there's enough consensus to make an 'official' one. E.g., how can we define separate wriggling and stretching animations, and then provide combinators for sequencing the two or even performing both at the same time?
>> var elem = document.getElementById('foo');
>> var a = anim(power(3.0))(-30.0, 0.0);
>> var posB = liftB(a, moveB(clicksE(elem), 25, 250));
>> insertValueB(liftB(function(x) {return x + "em";}, posB), elem, 'style', 'left');
> Implementation details:
>> var anim = function(f) {
>> return function(a,b) {
>> return function(x) { return a + (b - a) * f(x); }
>> }
>> }
>> // an example easing function
>> var power = function(y) { return function(x) { return Math.pow(x,y); } }
>> // moveB :: EventStream a * [Behavior] Int * [Behavior] Int -> Behavior Float
>> // very much like what Arjun showed me months ago
>> function moveB(ev,step,duration) {
>> var timerB = timerB(step);
>> var startB = startsWith(snapshotE(ev,timeB), 0);
>> return liftB(function(a,b,d) {return Math.min((b - a)/d, 1.0);},
>> startB, timeB, duration);
>> }
> I tried to abstract some details out, by defining a bunch of functions analogous
> to:
>> // one function for one animatable css property :)
>> var left = function(b, elem) {
>> insertValueB(b, elem, 'style', 'left');
>> return elem;
>> }
> (and fiddling with anim function), so they could be composed and abstracted out,
> just like in Fran. Then I noticed that insertValueB doesn't return anything, and
> an inquiry popped up.
> Cheers,
> Artyom Shalkhakov.
> PS sorry for posting so much code
Code examples are good :)
Hopefully the example at the beginning makes it clear that we want assignment statements and how they mix with the control flow to be super obvious. The proposal to mix data flow and assignments seems like a step backwards (though not a huge one!). E.g., it would suggest the following is idiomatic Flapjax:
Not needing names is often nice... but it can introduce noise when trying to understand a particular value, as in this example. Furthermore, rather than seeing assignments on the left, they're nestled into arbitrary terms! Can you show how you used your function 'left' that makes code clearer with such a shortform?
I agree that writing multiple 'insertValue' statements isn't ideal. However, you could use one insertValue statement to bind multiple ones by passing in an object with fields! E.g., insertValueB({x:timer_b(), y: timer_b()}, widget, 'style'). Similarly, I think an animation library can be defined by passing around the property object ( {x:.., y:...}), where adding an animation creates a new one, with the last composition being chosen to be inserted. This style might also leave a lot of room for optimization, just like what happened for inplace DOM updates :)
> This is really a pedantic red herring for the original question ... > [skipped] > In the first example, you have the full > definition: f(time(), odds, evens). In the second example, you have to > look around the entire program.
Wow. Thanks for clarifiying your terminology.
> An animation and widget library would make a lot of sense :) We've all > been rolling our own individually so far it seems, and I don't know if > there's enough consensus to make an 'official' one.
Well, I've looked into CSS3 Transitions and Animation modules, and it seems to me that it would be not too hard to implement these proposals using Flapjax (in fact, I already wrote a fraction of it). They are very limited in scope, though.
Also, I can volunteer to write an 'official' animation library that nobody would be happy with. :)
> E.g., how can we define separate wriggling and stretching animations, and then provide > combinators for sequencing the two or even performing both at the same time?
Good question. Is anybody willing to answer?
> The proposal to mix data flow and assignments seems like a step > backwards (though not a huge one!). E.g., it would suggest the following > is idiomatic Flapjax:
> Not needing names is often nice... but it can introduce noise when > trying to understand a particular value, as in this example.
Right. I don't understand what this code means.
> Furthermore, rather than seeing assignments on the left, they're nestled > into arbitrary terms! Can you show how you used your function 'left' > that makes code clearer with such a shortform?
Unfortunately, no, because I haven't actually used it yet. I wanted answers to my questions first.
> I agree that writing multiple 'insertValue' statements isn't ideal. > However, you could use one insertValue statement to bind multiple ones > by passing in an object with fields! E.g., insertValueB({x:timer_b(), y: > timer_b()}, widget, 'style'). Similarly, I think an animation library > can be defined by passing around the property object ( {x:.., y:...}), > where adding an animation creates a new one, with the last composition > being chosen to be inserted. This style might also leave a lot of room > for optimization, just like what happened for inplace DOM updates :)
This answers my question and yet leaves animation code pure, great!
The description early in section 2 should make Leo's point through a different example.
-----
We'd love to see your animation library. What to do about wriggling and stretching? I'd say let's not think about it too much; give it a try and toss out code; we'd all be happy to try it out and comment!
-----
[Along these lines, there's an nice "reactive widget library" embedded in one of our applications. We should extract and document that, too.]
> The description early in section 2 should make Leo's point through a > different example.
I guess you're talking about subsection 2.1, which is shows the structure of a typical JavaScript program. (Oops, I've missed the bug.)
> We'd love to see your animation library. What to do about wriggling > and stretching? I'd say let's not think about it too much; give it a > try and toss out code; we'd all be happy to try it out and comment!
Okay, will do, but most of it is contained within the code snippet that I posted, anyway. I need to ship my work first, so expect a delay.
> [Along these lines, there's an nice "reactive widget library" embedded > in one of our applications. We should extract and document that, too.]
On Apr 12, 9:23 am, Leo Meyerovich <lmeye...@gmail.com> wrote:
> I agree that writing multiple 'insertValue' statements isn't ideal.
> However, you could use one insertValue statement to bind multiple ones
> by passing in an object with fields! E.g., insertValueB({x:timer_b(), y:
> timer_b()}, widget, 'style').
This is not valid Flapjax, at least in 2.0. In particular you can't
pass an object with fields as the first argument to insertValue(E|B).
Can anyone comment on the idiomatic way to achieve the same effect?
I've been writing lots of curried setter functions to achieve the same
effect. Note that in the new Flapjax you can't see style properties
with insertValue, as you need to dereference two arrays (style, and
the particular property within that style.)
On Tue, Apr 14, 2009 at 10:28 AM, Noel Welsh <noelwe...@gmail.com> wrote:
> On Apr 12, 9:23 am, Leo Meyerovich <lmeye...@gmail.com> wrote:
>> I agree that writing multiple 'insertValue' statements isn't ideal.
>> However, you could use one insertValue statement to bind multiple ones
>> by passing in an object with fields! E.g., insertValueB({x:timer_b(), y:
>> timer_b()}, widget, 'style').
> This is not valid Flapjax, at least in 2.0. In particular you can't
> pass an object with fields as the first argument to insertValue(E|B).
> Can anyone comment on the idiomatic way to achieve the same effect?
> I've been writing lots of curried setter functions to achieve the same
> effect. Note that in the new Flapjax you can't see style properties
> with insertValue, as you need to dereference two arrays (style, and
> the particular property within that style.)
> (Yeah, I'm also working on an animation library.)
Shriram Krishnamurthi wrote:
> Some internal email going on about this. We'll respond soon.
> Shriram
> On Tue, Apr 14, 2009 at 10:28 AM, Noel Welsh <noelwe...@gmail.com> wrote:
>> On Apr 12, 9:23 am, Leo Meyerovich <lmeye...@gmail.com> wrote:
>>> I agree that writing multiple 'insertValue' statements isn't ideal.
>>> However, you could use one insertValue statement to bind multiple ones
>>> by passing in an object with fields! E.g., insertValueB({x:timer_b(), y:
>>> timer_b()}, widget, 'style').
>> This is not valid Flapjax, at least in 2.0. In particular you can't
>> pass an object with fields as the first argument to insertValue(E|B).
>> Can anyone comment on the idiomatic way to achieve the same effect?
>> I've been writing lots of curried setter functions to achieve the same
>> effect. Note that in the new Flapjax you can't see style properties
>> with insertValue, as you need to dereference two arrays (style, and
>> the particular property within that style.)
>> (Yeah, I'm also working on an animation library.)
2. Compiler mode (first example) vs. library mode (second example). In the library mode, I had to explicitly wait until the document finished loading before modifying the 'blah' element.
--
It's unclear what should happen if two insertValueB calls conflict. Always using the first style decreases the chance of this occurring, though I find using property objects to be clearer. One last bit of caution relating to the property object form is for deleting properties, which isn't handled cleanly. Overall, for 'normal' code, the object form is cleaner, but you should be cognizant of the semantic ambiguities.
> Shriram Krishnamurthi wrote:
>> Some internal email going on about this. We'll respond soon.
>> Shriram
>> On Tue, Apr 14, 2009 at 10:28 AM, Noel Welsh <noelwe...@gmail.com> >> wrote:
>>> On Apr 12, 9:23 am, Leo Meyerovich <lmeye...@gmail.com> wrote:
>>>> I agree that writing multiple 'insertValue' statements isn't ideal.
>>>> However, you could use one insertValue statement to bind multiple ones
>>>> by passing in an object with fields! E.g., >>>> insertValueB({x:timer_b(), y:
>>>> timer_b()}, widget, 'style').
>>> This is not valid Flapjax, at least in 2.0. In particular you can't
>>> pass an object with fields as the first argument to insertValue(E|B).
>>> Can anyone comment on the idiomatic way to achieve the same effect?
>>> I've been writing lots of curried setter functions to achieve the same
>>> effect. Note that in the new Flapjax you can't see style properties
>>> with insertValue, as you need to dereference two arrays (style, and
>>> the particular property within that style.)
>>> (Yeah, I'm also working on an animation library.)
when a mouseLeft event fires, there may not be a corresponding mouseTop event. So, we can't compute a new value for style. (We'd have to do some exceedingly clever partial update to just set style.left but not style.right. We're not that clever and that is not consistent in general.)
In the examples you gave:
> insertValueB works with a hash table as its first argument. The > following example code works:
Flapjax treats hash tables of constants as constant behaviors and hashtables with behaviors as reactive objects. The alternative would involve writing constantB(10), constantB("hello"), etc. That's why this example works.
However, Flapjax does not implicitly convert objects and constant values to event streams. So, in this function: