Hacker News new | ask | show | jobs
by seanmcdirmid 4446 days ago
Explicitly manipulating events as a stream has performance problems; mainly time leaks. Another issue is that it makes debugging really difficult, as you've now converted much of your control flow (that you could step through) into data flow (that you cannot), while there is very little progress on building good data flow debuggers.

Finally, events are often manipulated in very tricky ways. Take the problem of implementing a drag adapter: you have to remember to capture the mouse on the down event and release your capture on the up event, but then you also need to capture widget and mouse positions on the down event so you can compute deltas that avoid sampling resolution problems on the move events. Rx always gets these two points wrong, which is very annoying, but they have to fudge it otherwise event stream processing can't win in elegance.

1 comments

I haven't done any serious FRP, but thinking about your example of the drag adapter, wouldn't it be possible to do something like the following?

  - Declare a stream consisting of the composite of two stream: a mouse down and a mouse up
  - Map over the mouse down portion, transforming into an (x, y) coordinate
  - Map over the mouse up portion, transforming into an (x, y) coordinate
  - Produce a tuple of the two values
  - Use the resulting signal that determines what to do with the drag
In Bacon.js, I think it would look something like this (haven't tested it):

  var makeCoordinate = function(v) { return {x: v.clientX, y: v.clientY}; }
  var mergeStreams = function(v1, v2) { return {down: makeCoordinate(v1), up: makeCoordinate(v2)}; };

  var $html = $('html');
  var mousedownStream = $html.asEventStream('mousedown');
  var mouseupStream = $html.asEventStream('mouseup');
  var dragStream = mousedownStream.flatMap(function() {
    // Ensure that we only sample a single mousedown/mouseup pair.
    return Bacon.zipWith([mousedownStream, mouseupStream], mergeStreams).
        takeUntil(mouseupStream);
  };
I don't mean to be pedantic - your point is well taken. This was definitely a mental exercise to write, and I have no experience debugging (though I imagine it would be difficult).

EDIT: For this particular example I actually made it more complicated than it needs to be. Example JS Fiddle here: http://jsfiddle.net/w6mCK/

Compare with the control flow heavy managed time [1] version:

  on widget.Mouse.Down:
  | var pw = widget.position
  | var pm = widget.Mouse.Position
  after:
  | widget.Mouse.Capture()
  | widget.position = pw + (widget.Mouse.Position - pm)
  | on widget.Mouse.Up:
  | | widget.position = widget.position # freeze widget position
  | | break                             # stop the after block so capture/dragging stops
[1] http://research.microsoft.com/pubs/211297/managedtime.pdf