danschultz / frappe

A Dart package for functional reactive programming

Home Page:http://pub.dartlang.org/packages/frappe

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Comparing stream elements to properties?

radicaled opened this issue · comments

So, I have some code that, right now, looks like this:

class Project {
  List<Document> documents = [];
}

class Document {
  String id;
  Project project;
  String name;
}

class Context {
  Property<Project> currentProject;
  Context() {
    /* wire up currentProject to a stream, etc */
  }
}

class Persistence {
  // This stream is populated by concrete "Document" objects
  // that originate from a database somewhere.
  // Any changed Document, whether it belongs to the currently active Project or not,
  // gets sent down this stream.
  Stream<Document> changes;
}

class LiveRenderer {
  Stream<Document> stream;
  LiveRenderer(this.stream);
}

var context = new Context();
var persistence = new Persistence();
var currentProject;
var es = new EventStream(persistence.changes)
  // Filter out unrelated documents
  .where((document) => document.project == currentProject);

context.currentProject.listen((project) {
  currentProject = project;
});

// Renders individual Documents as they come into the string, 
// but only if their Project makes the current Project loaded in our context
liveRenderer = new LiveRenderer(es)
  ..watch();

This is the block that's troubling me, architecturally:

var currentProject;
var es = new EventStream(persistence.changes)
  // Filter out unrelated documents
  .where((document) => document.project == currentProject);

context.currentProject.listen((project) {
  currentProject = project;
});

I was hoping for an API that would let me do something like this, instead:

var es = new EventStream(persistence.changes)
  // Filter out unrelated documents
  .acceptOnly((document) => new Property.constant(document.project).equals(context.currentProject));

I can't seem to find the correct idiom in frappe to enable this, assuming one exists. I could be going about this the wrong way, as well -- this is my first foray into FRP-style development.

Am I doing this wrong? Is the pattern I'm using here architecturally incorrect, or is this something t hat frappe doesn't support yet?

You have a couple options here. Use combine or flatMapLatest.

Since I'm assuming you want to pass through the document that has changed, I'd probably lean towards using flatMapLatest.

You generally use the combine method when you need to perform some operation on the values of two streams/signals and return its result.

Example: flatMapLatest

var changes = new EventStream(persistence.changes);
var relaventChanges = changes.flatMapLatest((document) {
  return context.currentProject
      .where((project) => project == document.project)
      .map((_) => document);
});

Example: combine

var changes = new EventStream(persistence.changes);
var relaventChanges = changes
    .combine(context.currentProject, (a, b) => [a, b])
    .where((combined) => combined.first == combined.last)
    .map((combined) => combined.first);

Thinking more about this, you definitely want to use flatMapLatest. If you used combine, you could have events that get delivered improperly. For instance if a document change happens but doesn't match the current project, then current project changes to match the persisted document, it would trigger an event on relevantChanges.

My example for flatMapLatest is also incorrect. Here's probably what you'd want:

var changes = new EventStream(persistence.changes);
var relaventChanges = context.currentProject.flatMapLatest((project) {
  return changes.where((document) => document.project == project);
});

This is very helpful. I gotta get used to using flatmaplatest like that. I
would have been far mor likely to use combine
On Wed, 4 Mar 2015 at 1:26 pm, Dan Schultz notifications@github.com wrote:

Thinking more about this, you definitely want to use flatMapLatest. If
you used combine, you could have events that get delivered improperly.
For instance if a document change happens but doesn't match the current
project, then current project changes to match the persisted document, it
would trigger an event on relevantChanges.

My example for flatMapLatest is also incorrect. Here's probably what
you'd want:

var changes = new EventStream(persistence.changes);var relaventChanges = context.currentProject.flatMapLatest((project) {
return changes.where((document) => document.project == project);
});

Reply to this email directly or view it on GitHub
#36 (comment).

@danschultz thanks, I think that's exactly what I need.

Also, this diagram really helped me understand how flatMapLatest works: https://github.com/baconjs/bacon.js/wiki/Diagrams#flatmaplatest (for anyone as inexperienced as I am).

Thanks for the share, marble diagrams really help visualize this stuff.

BTW, would a wiki page that documents all the transformation methods be helpful to you? Or is the dart docs enough?

The documentation in 0.4 is really great -- I was looking at 0.3 since that's what my project is still on, and I couldn't figure out what flatMapLatest was doing.

I'm honestly not sure whether I would have needed the diagram or further explaination if I had looked at the documentation for 0.4 first, though.