Friday, November 21, 2014

Beware of Lazy Evaluation

A funny thing happened to me when I was presenting Bacon.js at the great Oredev conference a few weeks ago. You could say my own dogs bit me. What happened was that I used map to get the current value of a text field to be associated with a Bacon event.
The thing is, methods such as map and the combine use lazy evaluation to avoid evaluating values that aren't actually needed. This can be generally considered a Good Thing, but it has it's pitfalls.
If you pass a function that referentially transparent, you'll be fine. This means that your function should return the same value regardless of when it's called.
On the other hand, if you pass a function that returns a value depending on time, you may have problems. Consider a property contents that's derived from events like below.
var items = clicks.map(getCurrentValueFromUI).toProperty()
var submittedItems = items.sampledBy(submitClick)
Now the submittedItems stream will produce the current value of the items property when an event occurs in the submitClick stream. Or so you'd think. In fact, the value of submittedItems is evaluated at the time of the event in the submitClick stream, which means that it will actually produce the value of getCurrentValueFromUI at that time, instead of at the time of the originalclick event.
To force evaluation at the time of original event, you can just use flatMap instead of map. As in here.
var items = clicks.flatMap(getCurrentValueFromUI).toProperty()
So there I was, on the stage, looking stupid because of lazy evaluation. 
Now, looking stupid in places is what I do every day but, if it lazy evaluation fools me (the guy who wrote it), maybe it's not reasonable to expect new Bacon.js users to do well with lazy eval.
So, I opened an issue on Github where I kinda suggest that we should remove lazy evaluation in Bacon.js 0.8. What do you think?
Oh, and should I go to Minsk to the Rolling Scopes conference next year?

No comments:

Post a Comment