xebia-functional / macroid

A modular functional UI language for Android
527 stars 37 forks source link

FRP FTW #7

Open stanch opened 11 years ago

stanch commented 11 years ago

Implement event streams for widgets, sensors and gestures.

Related readings:

[1] http://infoscience.epfl.ch/record/176887/files/DeprecatingObservers2012.pdf [2] http://people.seas.harvard.edu/~chong/pubs/pldi13-elm.pdf

stanch commented 11 years ago

The ground for this has been prepared with https://github.com/stanch/macroid/commit/228c30592e05a8168c9bb6d3ba5328f65f78fb00#L1R34

stanch commented 11 years ago

The suggested syntax for widgets is myButton.sourceOf.clicks, where sourceOf or srcOf returns a Dynamic object and clicks triggers a search for setOnClickListener. Gotta be careful with dismisses though!

stanch commented 10 years ago

Maintaining the same EventStream handler for a given widget proved to be non-trivial. Besides, the implementation will depend on a particular frp library. Therefore closing it here in hope for a separate macroid-frp project.

nightscape commented 9 years ago

Hi Nick,

the documentation still contains the scala.frp example with a reference to a macroid-frp project which I couldn't find. I saw that you made some updates to scala-frp and have an example application using scala-rx. What are your current plans regarding FRP in Macroid? Which one should I use currently?

Best Martin

stanch commented 9 years ago

Hey,

About a month ago I found some time to think about the design of event streams for widgets. I started the work on this branch: https://github.com/macroid/macroid/tree/ft-refactor-concurrency (never mind the misleading name).

That branch adds things I called “excerpts”, which extract information from widgets: https://github.com/macroid/macroid/blob/ft-refactor-concurrency/macroid-core/src/main/scala/macroid/Creatures.scala#L75. From the tests:

"Excerpting" should "work with widgets and excerpts" in {
  def foo: Ui[CharSequence] = {
    w[Button] ~> getText
  }
}

The next idea is to create an excerpt that would inject a specially crafted listener and return an EventStream, like so:

val clickEvents: Ui[EventStream[View]] = w[Button] ~> Events.click()

// under the hood, generated by a macro
...
trait EventStreamListener[E] {
  def source: EventSource[E]
}
...
button.getOnClickListener match {
  case l: EventStreamListener[View] => l.source
  case _ =>
    val listener = new View.OnClickListener with EventStreamListener[View] {
      def source = EventSource[View]()
      def onClick(view: View) = source.fire(view)
    }
    button.setOnClickListener(listener)
    listener.source
}
...

Unfortunately I bumped into issues with IDEA, then I went on a vacation, and currently most of my time is dedicated to the day job.

Right now you could copy the definitions from the ScalaDays example, the main reason I have not added them to the codebase is that I’m not sure it’s the best way to handle memory management (i.e. who stores the strong reference to the event handler). One alternative approach would be to store the reference inside the widget (in a tag). Ideally it should also be possible to cancel the updates.

Overall, event streams and reactive variables solve two complemental problems. I hope to find some time in the few upcoming weeks to finalize the stream implementation above, which will reside in a macroid-frp module inside this project. Maybe it will also include support for scala.rx found in the ScalaDays example. If you have any thoughts on either, I’d greatly appreciate them :)