This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Reactive updates coming soon בע"ה

14 replies
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Recently I've been working on some significant additions and changes to reactive, with G-d's help. They should hit github soon but first I want to give a heads up and get feedback.
1. DeltaSeqFirst, a major, breaking change in reactive-core, regarding increment updates, a.k.a. SeqSignal. In the past, the way this worked was that if you wroteval s1 = BufferSignal(1, 2, 3) val s2 = s1 map (n => <div>{n}</div>)
you would get a SeqSignal[Int], which has an EventStream[SeqDelta[Int,Int]], which allows you to only handle changes to the list rather than the whole thing at a time. In conjuction with reactive-web's Repeater, you can then easily and efficiently map a list of data to html content.
This is the major issue:First of all, is that the backing Seq, TransformedSeq, was not written so well. For instance, the function passed to map would be called on every change for every item in the list, as well as for the deltas. This is particularly an issue if the function causes side effects (which reactive-web is not yet free of). Secondly, take this scenario:mySeqSignal.map(xs => if(xs.length>10) xs.filter(_ < 10) else xs.filter(_ > 10))

The way map(x => SeqSignal) worked was a bit magical: Pass a TransformedSeq to the function so it returns a TransformedSeq, and have the TransformedSeq transform future deltas according to the operation it represents (filter in the above case). Clearly since we don't know if it will be the same operation in the future (over 10 or under 10?), we must always run the whole function. Cleverly computing the deltas then becomes pretty pointless. So the inefficiency in the first point is baked in.
Also, flatMap(x => SeqSignal) returned a SeqSignal, but the implementation didn't really work (at this point I'm not sure it makes any sense) so I ended up falling back to diffing the two values, making it pointless (you can do this yourself with the SeqSignal.apply factory).
The new way is this:Signal.map and .flatMap always returns a plain Signal. If you want a smart, efficient transformation of deltas, you do this:mySeqSignal.now.filter(_ < 10).signal
That's it. There's no way to change the transformation on the fly now. The transformed (i.e. filtered) seq computes its contents in a lazy val based on the transformed deltas, so it should be very efficient. If you need to change operations then you need it to diff the values, so just write SeqSignal(myPlainSignal.map(xs => if(xs.length>10)xs.filter(_<10) else xs.filter(_>10))).
The new version of TransformedSeq is called DeltaSeq. TransformedSeq was removed.

2. WidgetsA new subpackage in reactive-web for higher-level widgets that combine smaller building blocks into useful packages. Currently this includes Messages, a simple way to give modeless, closeable popups. The provided css (you must include it manually) overlays them translucently over the page, stacked vertically. Also being worked on with G-d's help are a table view with sortable headers, a table editor (extends the table view) with undo/redo and "unsaved changes" feedback (hopefully soon, paginating and filtering), and (perhaps in the web package) simple access to common editors, and visual validity and modified feedback.


3. Javascript FunctionsNow reactive-web's type-safe Javascript DSL includes the function statement, and supports function literals containing statements (currently only unary functions, if you need more just ask):
Function statement, custom named:       object myFunc extends Function({ x: $[JsNumber] =>        If(x > 10) {          window alert "Greater"         } Else {          window alert "Small"        }      })      myFunc(10)Autogenerated name (requires a Page):        val myFunc2 = Function({ x: $[JsNumber] => Return(x > 10) })
Function literals (anonymous functions):    { x: $[JsNumber] =>      If(x > 10) {        window alert "Greater"      } Else {         window alert "Small"      }    }.$

4. Improved Javascript AssignmentsYou can now write to properties of JsStub objects. So for instance you can write window.onbeforeunload := {x: $[JsObj] => /* see above on function literals */ }window.onbeforeunload happens to be provided with the library. Here's how you can set it up for any javascript API: trait obj extends JsStub {  val prop: Assignable[JsNumber]}val obj = $$[obj]Javascript {  obj.prop := 25}You can also assign values to arrays or object/maps: Javascript {  val evt = JsVar[JsTypes.JsObj]()  evt.get("returnValue") := reply}

5. Signal.flatMap(x => EventStream): EventStream You can now pass an EventStream-returning function to Signal.flatMap, returning an EventStream. So e.g.:val v = Var(x)val c: EventStream[Unit] = mouseClicks // hypotheticalval fm: EventStream[X] = for(v0 <- v; c0 <- c) yield v0  // whenever there's a click, fire the current setting

6. Signal.foldLeftSignal now has foldLeft, just like EventStream, allowing you take the past into account.

Diego Medina
Joined: 2010-03-28,
User offline. Last seen 42 years 45 weeks ago.
Re: [Lift] Reactive updates coming soon בע"ה

All the changes sound very interesting, thanks for continuing with the project!

Diego

On Sun, Jan 8, 2012 at 1:25 AM, Naftoli Gugenheim wrote:
> Recently I've been working on some significant additions and changes to
> reactive, with G-d's help. They should hit github soon but first I want to
> give a heads up and get feedback.
>
> 1. DeltaSeq
> First, a major, breaking change in reactive-core, regarding increment
> updates, a.k.a. SeqSignal. In the past, the way this worked was that if you
> wrote
> val s1 = BufferSignal(1, 2, 3)
> val s2 = s1 map (n => {n})
>
> you would get a SeqSignal[Int], which has an EventStream[SeqDelta[Int,Int]],
> which allows you to only handle changes to the list rather than the whole
> thing at a time. In conjuction with reactive-web's Repeater, you can then
> easily and efficiently map a list of data to html content.
>
> This is the major issue:
> First of all, is that the backing Seq, TransformedSeq, was not written so
> well. For instance, the function passed to map would be called on every
> change for every item in the list, as well as for the deltas. This is
> particularly an issue if the function causes side effects (which
> reactive-web is not yet free of).
> Secondly, take this scenario:
> mySeqSignal.map(xs => if(xs.length>10) xs.filter(_ < 10) else xs.filter(_ >
> 10))
>
> The way map(x => SeqSignal) worked was a bit magical: Pass a TransformedSeq
> to the function so it returns a TransformedSeq, and have the TransformedSeq
> transform future deltas according to the operation it represents (filter in
> the above case). Clearly since we don't know if it will be the same
> operation in the future (over 10 or under 10?), we must always run the whole
> function. Cleverly computing the deltas then becomes pretty pointless. So
> the inefficiency in the first point is baked in.
>
> Also, flatMap(x => SeqSignal) returned a SeqSignal, but the implementation
> didn't really work (at this point I'm not sure it makes any sense) so I
> ended up falling back to diffing the two values, making it pointless (you
> can do this yourself with the SeqSignal.apply factory).
>
> The new way is this:
> Signal.map and .flatMap always returns a plain Signal. If you want a smart,
> efficient transformation of deltas, you do this:
> mySeqSignal.now.filter(_ < 10).signal
>
> That's it. There's no way to change the transformation on the fly now. The
> transformed (i.e. filtered) seq computes its contents in a lazy val based on
> the transformed deltas, so it should be very efficient.
> If you need to change operations then you need it to diff the values, so
> just write SeqSignal(myPlainSignal.map(xs => if(xs.length>10)xs.filter(_<10)
> else xs.filter(_>10))).
>
> The new version of TransformedSeq is called DeltaSeq. TransformedSeq was
> removed.
>
>
> 2. Widgets
> A new subpackage in reactive-web for higher-level widgets that combine
> smaller building blocks into useful packages. Currently this includes
> Messages, a simple way to give modeless, closeable popups. The provided css
> (you must include it manually) overlays them translucently over the page,
> stacked vertically. Also being worked on with G-d's help are a table view
> with sortable headers, a table editor (extends the table view) with
> undo/redo and "unsaved changes" feedback (hopefully soon, paginating and
> filtering), and (perhaps in the web package) simple access to common
> editors, and visual validity and modified feedback.
>
>
>
> 3. Javascript Functions
> Now reactive-web's type-safe Javascript DSL includes the function statement,
> and supports function literals containing statements (currently only unary
> functions, if you need more just ask):
>
> Function statement, custom named:
>        object myFunc extends Function({ x: $[JsNumber] =>
>         If(x > 10) {
>           window alert "Greater"
>         } Else {
>           window alert "Small"
>         }
>       })
>       myFunc(10)
> Autogenerated name (requires a Page):
>         val myFunc2 = Function({ x: $[JsNumber] => Return(x > 10) })
>
> Function literals (anonymous functions):
>     { x: $[JsNumber] =>
>       If(x > 10) {
>         window alert "Greater"
>       } Else {
>         window alert "Small"
>       }
>     }.$
>
>
> 4. Improved Javascript Assignments
> You can now write to properties of JsStub objects. So for instance you can
> write
> window.onbeforeunload := {x: $[JsObj] => /* see above on function literals
> */ }
> window.onbeforeunload happens to be provided with the library. Here's how
> you can set it up for any javascript API:
> trait obj extends JsStub {
>   val prop: Assignable[JsNumber]
> }
> val obj = $$[obj]
> Javascript {
>   obj.prop := 25
> }
> You can also assign values to arrays or object/maps:
> Javascript {
>   val evt = JsVar[JsTypes.JsObj]()
>   evt.get("returnValue") := reply
> }
>
>
> 5. Signal.flatMap(x => EventStream): EventStream
> You can now pass an EventStream-returning function to Signal.flatMap,
> returning an EventStream. So e.g.:
> val v = Var(x)
> val c: EventStream[Unit] = mouseClicks // hypothetical
> val fm: EventStream[X] = for(v0 <- v; c0 <- c) yield v0  // whenever there's
> a click, fire the current setting
>
>
> 6. Signal.foldLeft
> Signal now has foldLeft, just like EventStream, allowing you take the past
> into account.
>
>
> --
> Lift, the simply functional web framework: http://liftweb.net
> Code: http://github.com/lift
> Discussion: http://groups.google.com/group/liftweb
> Stuck? Help us help you:
> https://www.assembla.com/wiki/show/liftweb/Posting_example_code

Sophie
Joined: 2011-11-10,
User offline. Last seen 42 years 45 weeks ago.
Re: [Lift] Reactive updates coming soon ??"?

This project sounds really good. Could you tell a bit about performance
/ various leak-like issues that FRP can face?

Thanks!

On 2012-01-08 00:25:25 -0600, Naftoli Gugenheim said:

> Recently I've been working on some significant additions and changes to
> reactive, with G-d's help. They should hit github soon but first I want
> to give a heads up and get feedback.
>
> 1. DeltaSeq
> First, a major, breaking change in reactive-core, regarding increment
> updates, a.k.a. SeqSignal. In the past, the way this worked was that if
> you wrote
> val s1 = BufferSignal(1, 2, 3)
> val s2 = s1 map (n => {n})
>
> you would get a SeqSignal[Int], which has an
> EventStream[SeqDelta[Int,Int]], which allows you to only handle changes
> to the list rather than the whole thing at a time. In conjuction with
> reactive-web's Repeater, you can then easily and efficiently map a list
> of data to html content.
>
> This is the major issue:
> First of all, is that the backing Seq, TransformedSeq, was not written
> so well. For instance, the function passed to map would be called on
> every change for every item in the list, as well as for the deltas.
> This is particularly an issue if the function causes side effects
> (which reactive-web is not yet free of).
> Secondly, take this scenario:
> mySeqSignal.map(xs => if(xs.length>10) xs.filter(_ < 10) else
> xs.filter(_ > 10))
>
> The way map(x => SeqSignal) worked was a bit magical: Pass a
> TransformedSeq to the function so it returns a TransformedSeq, and have
> the TransformedSeq transform future deltas according to the operation
> it represents (filter in the above case). Clearly since we don't know
> if it will be the same operation in the future (over 10 or under 10?),
> we must always run the whole function. Cleverly computing the deltas
> then becomes pretty pointless. So the inefficiency in the first point
> is baked in.
>
> Also, flatMap(x => SeqSignal) returned a SeqSignal, but the
> implementation didn't really work (at this point I'm not sure it makes
> any sense) so I ended up falling back to diffing the two values, making
> it pointless (you can do this yourself with the SeqSignal.apply
> factory).
>
> The new way is this:
> Signal.map and .flatMap always returns a plain Signal. If you want a
> smart, efficient transformation of deltas, you do this:
> mySeqSignal.now.filter(_ < 10).signal
>
> That's it. There's no way to change the transformation on the fly now.
> The transformed (i.e. filtered) seq computes its contents in a lazy val
> based on the transformed deltas, so it should be very efficient.
> If you need to change operations then you need it to diff the values,
> so just write SeqSignal(myPlainSignal.map(xs =>
> if(xs.length>10)xs.filter(_<10) else xs.filter(_>10))).
>
> The new version of TransformedSeq is called DeltaSeq. TransformedSeq
> was removed.
>
>
> 2. Widgets
> A new subpackage in reactive-web for higher-level widgets that combine
> smaller building blocks into useful packages. Currently this includes
> Messages, a simple way to give modeless, closeable popups. The provided
> css (you must include it manually) overlays them translucently over the
> page, stacked vertically. Also being worked on with G-d's help are a
> table view with sortable headers, a table editor (extends the table
> view) with undo/redo and "unsaved changes" feedback (hopefully soon,
> paginating and filtering), and (perhaps in the web package) simple
> access to common editors, and visual validity and modified feedback.
>
>
>
> 3. Javascript Functions
> Now reactive-web's type-safe Javascript DSL includes the function
> statement, and supports function literals containing statements
> (currently only unary functions, if you need more just ask):
>
> Function statement, custom named:
>        object myFunc extends Function({ x: $[JsNumber] =>
>         If(x > 10) {
>           window alert "Greater"
>         } Else {
>           window alert "Small"
>         }
>       })
>       myFunc(10)
> Autogenerated name (requires a Page):
>         val myFunc2 = Function({ x: $[JsNumber] => Return(x > 10) })
>
> Function literals (anonymous functions):
>     { x: $[JsNumber] =>
>       If(x > 10) {
>         window alert "Greater"
>       } Else {
>         window alert "Small"
>       }
>     }.$
>
>
> 4. Improved Javascript Assignments
> You can now write to properties of JsStub objects. So for instance you
> can write
> window.onbeforeunload := {x: $[JsObj] => /* see above on function literals */ }
> window.onbeforeunload happens to be provided with the library. Here's
> how you can set it up for any javascript API:
> trait obj extends JsStub {
>   val prop: Assignable[JsNumber]
> }
> val obj = $$[obj]
> Javascript {
>   obj.prop := 25
> }
> You can also assign values to arrays or object/maps:
> Javascript {
>   val evt = JsVar[JsTypes.JsObj]()
>   evt.get("returnValue") := reply
> }
>
>
> 5. Signal.flatMap(x => EventStream): EventStream
> You can now pass an EventStream-returning function to Signal.flatMap,
> returning an EventStream. So e.g.:
> val v = Var(x)
> val c: EventStream[Unit] = mouseClicks // hypothetical
> val fm: EventStream[X] = for(v0 <- v; c0 <- c) yield v0  // whenever
> there's a click, fire the current setting
>
>
> 6. Signal.foldLeft
> Signal now has foldLeft, just like EventStream, allowing you take the
> past into account.
>
>
>

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon ??"?
Can you be specific?I haven't benchmarked anything but in general it doesn't do anything too fancy that I'd expect it to have performance issues. Ultimately it's a bunch of thin wrappers around the imperative way of doing things. I believe the not-yet-published DeltaSeq (a rewrite of TransformedSeq, used from SeqSignal) may need some tuning though (right now it needs a bug fixed, actually --- that's why it's not published yet).
What do you mean by leaks? If you mean memory leak I copied Ingo's technique of using WeakRefernences and Observing reference-holders, so that shouldn't be a problem.
Otherwise, can you be more specific? Is there a project that you're considering using it for that's performance sensitive --- if you elaborate I may do a better job at ruling out your concern. :)

On Mon, Jan 9, 2012 at 11:23 AM, Sophie <itsme213@hotmail.com> wrote:
This project sounds really good. Could you tell a bit about performance / various leak-like issues that FRP can face?

Thanks!

On 2012-01-08 00:25:25 -0600, Naftoli Gugenheim said:

Recently I've been working on some significant additions and changes to reactive, with G-d's help. They should hit github soon but first I want to give a heads up and get feedback.

1. DeltaSeq
First, a major, breaking change in reactive-core, regarding increment updates, a.k.a. SeqSignal. In the past, the way this worked was that if you wrote
val s1 = BufferSignal(1, 2, 3)
val s2 = s1 map (n => <div>{n}</div>)

you would get a SeqSignal[Int], which has an EventStream[SeqDelta[Int,Int]], which allows you to only handle changes to the list rather than the whole thing at a time. In conjuction with reactive-web's Repeater, you can then easily and efficiently map a list of data to html content.

This is the major issue:
First of all, is that the backing Seq, TransformedSeq, was not written so well. For instance, the function passed to map would be called on every change for every item in the list, as well as for the deltas. This is particularly an issue if the function causes side effects (which reactive-web is not yet free of).
Secondly, take this scenario:
mySeqSignal.map(xs => if(xs.length>10) xs.filter(_ < 10) else xs.filter(_ > 10))

The way map(x => SeqSignal) worked was a bit magical: Pass a TransformedSeq to the function so it returns a TransformedSeq, and have the TransformedSeq transform future deltas according to the operation it represents (filter in the above case). Clearly since we don't know if it will be the same operation in the future (over 10 or under 10?), we must always run the whole function. Cleverly computing the deltas then becomes pretty pointless. So the inefficiency in the first point is baked in.

Also, flatMap(x => SeqSignal) returned a SeqSignal, but the implementation didn't really work (at this point I'm not sure it makes any sense) so I ended up falling back to diffing the two values, making it pointless (you can do this yourself with the SeqSignal.apply factory).

The new way is this:
Signal.map and .flatMap always returns a plain Signal. If you want a smart, efficient transformation of deltas, you do this:
mySeqSignal.now.filter(_ < 10).signal

That's it. There's no way to change the transformation on the fly now. The transformed (i.e. filtered) seq computes its contents in a lazy val based on the transformed deltas, so it should be very efficient.
If you need to change operations then you need it to diff the values, so just write SeqSignal(myPlainSignal.map(xs => if(xs.length>10)xs.filter(_<10) else xs.filter(_>10))).

The new version of TransformedSeq is called DeltaSeq. TransformedSeq was removed.


2. Widgets
A new subpackage in reactive-web for higher-level widgets that combine smaller building blocks into useful packages. Currently this includes Messages, a simple way to give modeless, closeable popups. The provided css (you must include it manually) overlays them translucently over the page, stacked vertically. Also being worked on with G-d's help are a table view with sortable headers, a table editor (extends the table view) with undo/redo and "unsaved changes" feedback (hopefully soon, paginating and filtering), and (perhaps in the web package) simple access to common editors, and visual validity and modified feedback.



3. Javascript Functions
Now reactive-web's type-safe Javascript DSL includes the function statement, and supports function literals containing statements (currently only unary functions, if you need more just ask):

Function statement, custom named:
       object myFunc extends Function({ x: $[JsNumber] =>
        If(x > 10) {
          window alert "Greater"
        } Else {
          window alert "Small"
        }
      })
      myFunc(10)
Autogenerated name (requires a Page):
        val myFunc2 = Function({ x: $[JsNumber] => Return(x > 10) })

Function literals (anonymous functions):
    { x: $[JsNumber] =>
      If(x > 10) {
        window alert "Greater"
      } Else {
        window alert "Small"
      }
    }.$


4. Improved Javascript Assignments
You can now write to properties of JsStub objects. So for instance you can write
window.onbeforeunload := {x: $[JsObj] => /* see above on function literals */ }
window.onbeforeunload happens to be provided with the library. Here's how you can set it up for any javascript API:
trait obj extends JsStub {
  val prop: Assignable[JsNumber]
}
val obj = $$[obj]
Javascript {
  obj.prop := 25
}
You can also assign values to arrays or object/maps:
Javascript {
  val evt = JsVar[JsTypes.JsObj]()
  evt.get("returnValue") := reply
}


5. Signal.flatMap(x => EventStream): EventStream
You can now pass an EventStream-returning function to Signal.flatMap, returning an EventStream. So e.g.:
val v = Var(x)
val c: EventStream[Unit] = mouseClicks // hypothetical
val fm: EventStream[X] = for(v0 <- v; c0 <- c) yield v0  // whenever there's a click, fire the current setting


6. Signal.foldLeft
Signal now has foldLeft, just like EventStream, allowing you take the past into account.



Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה
The updates are now on github. They won't be published yet because there's a failing test. If anyone wants to help, I'd really appreciate it!
git clone git://github.com/nafg/reactive.git sbt '~test-only reactive.DeltaSeqTests'
I wrote some pretty verbose comments in the relevant code (https://github.com/nafg/reactive/blob/master/reactive-core/src/main/scala/reactive/DeltaSeq.scala), but the gist is:
We have a DeltaSeq, a Seq that knows the the diff between it and its previous version. (This is for SeqSignals, a Signal whose value is a Seq and keeps track of the diffs from one value to the next so that derived objects can act on the incremental changes for greater efficiency. To see it in action go to http://www.reactive-web.co.cc/showdemo/RepeaterDemo, open Chrome Developer Tools or Firebug to watch network activity, and click the Add button on the page a few times.)
Now, suppose we want to say SeqSignal b = SeqSignal a dropWhile(_ < 10) etc. So if SeqSignal a has (0,5,10,15), SeqSignal b will have (10, 15).Now when SeqSignal a's value changes, e.g. to (10,0,5,10,15), we want SeqSignal b to say to its underlying DeltaSeq: Here's a's previous DeltaSeq and a's updated DeltaSeq, now compute the DeltaSeq that will replace you. It should be equivalent to List(10,0,5,10,15).dropWhile(_<10) but compute the result based on the deltas from a's last value to its current value and the dropWhile predicate.
Thank G-d I wrote some tests in scalacheck, it made it very easy to find a lot of bugs. Fixing them was not so easy. Thank G-d I got all the other operations working, but I haven't solved dropWhile yet.
If anyone can get it to work I'd really appreciate it!! You can see the relevant source code at https://github.com/nafg/reactive/blob/master/reactive-core/src/main/scala/reactive/DeltaSeq.scala (mainly lines 261-416, although see also the comment at line 56).
Thanks!!


On Mon, Jan 9, 2012 at 10:31 AM, Diego Medina <diego@fmpwizard.com> wrote:
All the changes sound very interesting, thanks for continuing with the project!

 Diego

On Sun, Jan 8, 2012 at 1:25 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
> Recently I've been working on some significant additions and changes to
> reactive, with G-d's help. They should hit github soon but first I want to
> give a heads up and get feedback.
>
> 1. DeltaSeq
> First, a major, breaking change in reactive-core, regarding increment
> updates, a.k.a. SeqSignal. In the past, the way this worked was that if you
> wrote
> val s1 = BufferSignal(1, 2, 3)
> val s2 = s1 map (n => <div>{n}</div>)
>
> you would get a SeqSignal[Int], which has an EventStream[SeqDelta[Int,Int]],
> which allows you to only handle changes to the list rather than the whole
> thing at a time. In conjuction with reactive-web's Repeater, you can then
> easily and efficiently map a list of data to html content.
>
> This is the major issue:
> First of all, is that the backing Seq, TransformedSeq, was not written so
> well. For instance, the function passed to map would be called on every
> change for every item in the list, as well as for the deltas. This is
> particularly an issue if the function causes side effects (which
> reactive-web is not yet free of).
> Secondly, take this scenario:
> mySeqSignal.map(xs => if(xs.length>10) xs.filter(_ < 10) else xs.filter(_ >
> 10))
>
> The way map(x => SeqSignal) worked was a bit magical: Pass a TransformedSeq
> to the function so it returns a TransformedSeq, and have the TransformedSeq
> transform future deltas according to the operation it represents (filter in
> the above case). Clearly since we don't know if it will be the same
> operation in the future (over 10 or under 10?), we must always run the whole
> function. Cleverly computing the deltas then becomes pretty pointless. So
> the inefficiency in the first point is baked in.
>
> Also, flatMap(x => SeqSignal) returned a SeqSignal, but the implementation
> didn't really work (at this point I'm not sure it makes any sense) so I
> ended up falling back to diffing the two values, making it pointless (you
> can do this yourself with the SeqSignal.apply factory).
>
> The new way is this:
> Signal.map and .flatMap always returns a plain Signal. If you want a smart,
> efficient transformation of deltas, you do this:
> mySeqSignal.now.filter(_ < 10).signal
>
> That's it. There's no way to change the transformation on the fly now. The
> transformed (i.e. filtered) seq computes its contents in a lazy val based on
> the transformed deltas, so it should be very efficient.
> If you need to change operations then you need it to diff the values, so
> just write SeqSignal(myPlainSignal.map(xs => if(xs.length>10)xs.filter(_<10)
> else xs.filter(_>10))).
>
> The new version of TransformedSeq is called DeltaSeq. TransformedSeq was
> removed.
>
>
> 2. Widgets
> A new subpackage in reactive-web for higher-level widgets that combine
> smaller building blocks into useful packages. Currently this includes
> Messages, a simple way to give modeless, closeable popups. The provided css
> (you must include it manually) overlays them translucently over the page,
> stacked vertically. Also being worked on with G-d's help are a table view
> with sortable headers, a table editor (extends the table view) with
> undo/redo and "unsaved changes" feedback (hopefully soon, paginating and
> filtering), and (perhaps in the web package) simple access to common
> editors, and visual validity and modified feedback.
>
>
>
> 3. Javascript Functions
> Now reactive-web's type-safe Javascript DSL includes the function statement,
> and supports function literals containing statements (currently only unary
> functions, if you need more just ask):
>
> Function statement, custom named:
>        object myFunc extends Function({ x: $[JsNumber] =>
>         If(x > 10) {
>           window alert "Greater"
>         } Else {
>           window alert "Small"
>         }
>       })
>       myFunc(10)
> Autogenerated name (requires a Page):
>         val myFunc2 = Function({ x: $[JsNumber] => Return(x > 10) })
>
> Function literals (anonymous functions):
>     { x: $[JsNumber] =>
>       If(x > 10) {
>         window alert "Greater"
>       } Else {
>         window alert "Small"
>       }
>     }.$
>
>
> 4. Improved Javascript Assignments
> You can now write to properties of JsStub objects. So for instance you can
> write
> window.onbeforeunload := {x: $[JsObj] => /* see above on function literals
> */ }
> window.onbeforeunload happens to be provided with the library. Here's how
> you can set it up for any javascript API:
> trait obj extends JsStub {
>   val prop: Assignable[JsNumber]
> }
> val obj = $$[obj]
> Javascript {
>   obj.prop := 25
> }
> You can also assign values to arrays or object/maps:
> Javascript {
>   val evt = JsVar[JsTypes.JsObj]()
>   evt.get("returnValue") := reply
> }
>
>
> 5. Signal.flatMap(x => EventStream): EventStream
> You can now pass an EventStream-returning function to Signal.flatMap,
> returning an EventStream. So e.g.:
> val v = Var(x)
> val c: EventStream[Unit] = mouseClicks // hypothetical
> val fm: EventStream[X] = for(v0 <- v; c0 <- c) yield v0  // whenever there's
> a click, fire the current setting
>
>
> 6. Signal.foldLeft
> Signal now has foldLeft, just like EventStream, allowing you take the past
> into account.
>
>
> --
> Lift, the simply functional web framework: http://liftweb.net
> Code: http://github.com/lift
> Discussion: http://groups.google.com/group/liftweb
> Stuck? Help us help you:
> https://www.assembla.com/wiki/show/liftweb/Posting_example_code



--
Diego Medina
Web Developer
diego@fmpwizard.com
http://www.fmpwizard.com

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה
To put the problem differently:
Given Seq a + <some inserts/removes> = Seq a', and Seq b = Seq.dropWhile(some condition), figure out Seq b', and the diff between b and b', without running the condition function more times than necessary. The easy way would be to just do a dropWhile on a', but that means running the predicate on elements it was already run on and then doing more diff calculation than necessary. The code is already written, it just has some bugs. I would really appreciate if anyone can fix it. Here's the code that implements the actual algorithm:

    /* * takeWhile and dropWhile both see a sequence as divided into a prefix of elements that pass a predicate, and the remaining elements, * beginning with the first element that fails the predicate. (It makes no difference whether subsequent elements pass it or fail it.) * The difference is that takeWhile returns the prefix, and dropWhile returns the remaining elements. * * Edits can occur within the prefix specified by the predicate, at its border, or outside it. * At its border means either removing the first element failing the predicate, inserting a new element at its index, or updating the value at its index. * * In the case of takeWhile, an edit occurring at the first element for which the predicate fails * can cause any number of elements to be included, if the element that will now follow the current prefix will pass the predicate. * In the case of dropWhile, it can cause any number of elements to be removed, if the element that will now follow the * prefix will pass the predicate. * * An edit more than one past the end of the prefix has no effect in the case of takeWhile; in the case of dropWhile, that edit is * applied after translating it (i - prefixLength). * * A remove, or insert or update whose new element passes the predicate, that occurs before the end of the prefix, has no effect in the case of dropWhile; * in the case of takeWhile, it can be applied directly. * * An insert or update within the prefix, whose new element fails the predicate, in the case of takeWhile is not applied, * and subsequent elements are removed, as the prefix has been shortened. * In the case of dropWhile, it and subsequent elements are inserted; since the prefix has been shortened, the set of remaining elements has expanded. * * prefixLength: The number of elements in a row at the beginning of the parent sequence that pass the predicate. Consequently, the first index * that is either outside of the parent sequence or whose element fails the predicate. * */     def updatedFromParent(parentUpdated: DeltaSeq[T]): PrefixWhile[T] = new PrefixWhile[T](parentUpdated, prev.take, prev.p.asInstanceOf[T => Boolean]) {       override lazy val (fromDelta, predValues, underlying) = {         val buf = prev.toList.toBuffer         val ds = new ArrayBuffer[SeqDelta[T, T]]         var prdVals = prev.predValues         def calcPrefixLength = {           def loop(n: Int, s: Stream[(T, Boolean)]): Int = if (s.isEmpty || !s.head._2) n else loop(n + 1, s.tail)           loop(0, prdVals)         }         var prefixLength = calcPrefixLength         def applyDelta(d: SingleDelta[T, T]): Unit = {           val oldPrefixLength = prefixLength           d match {             case Include(i, e) =>               val v = p(e)               prdVals = prdVals.patch(i, List(e -> v), 0)
              if (i < prefixLength) {                 if (v) prefixLength += 1 else prefixLength = i               } else if (v && i == prefixLength) {                 prefixLength = calcPrefixLength               }
              if (!take || v) {                 if (take && v && i <= prefixLength && i <= buf.length) {                   ds += Include(i, e)                   buf.insert(i, e)                 } else if (!take && i >= prefixLength) {                   val j = i - prefixLength                   println("Applying original Include at "+j)                   ds += Include(j, e)                   buf.insert(j, e)                 }               }
              if (prefixLength > oldPrefixLength) for (j <- oldPrefixLength + 1 until prefixLength) {                 if (take && j <= buf.length) {                   ds += Include(j, prdVals(j)._1)                   buf.insert(j, prdVals(j)._1)                 } else if (!take && j < buf.length) {                   ds += Remove(0, prdVals(oldPrefixLength)._1)                   buf.remove(0)                 }               }               else if (prefixLength < oldPrefixLength) for (j <- prefixLength until oldPrefixLength) {                 println("j: "+j)                 if (take && prefixLength < buf.length) {                   ds += Remove(prefixLength, prdVals(j)._1)                   buf.remove(prefixLength)                 } else if (!take && j - prefixLength <= buf.length) {                   ds += Include(j - prefixLength + 1, prdVals(j + 1)._1)                   buf.insert(j - prefixLength + 1, prdVals(j + 1)._1)                 }               }
            case Remove(i, o) =>               prdVals = prdVals.patch(i, Nil, 1)               if (i < prefixLength)                 prefixLength -= 1               else if (i == prefixLength)                 prefixLength = calcPrefixLength
              if (take && i <= prefixLength && i < buf.length) {                 ds += Remove(i, o)                 buf.remove(i)               } else if (!take && i >= prefixLength) {                 val j = i - prefixLength max 0                 if (j < buf.length) {                   println("Applying original Remove at "+j)                   ds += Remove(j, o)                   buf.remove(j)                 }               }
              if (prefixLength > oldPrefixLength) for (j <- oldPrefixLength until prefixLength) {                 if (take && j <= buf.length && (prdVals isDefinedAt j)) {                   ds += Include(j, prdVals(j)._1)                   buf.insert(j, prdVals(j)._1)                 } else if (!take && buf.nonEmpty && (prdVals isDefinedAt j)) {                   ds += Remove(0, prdVals(j)._1)                   buf.remove(0)                 }               }               else if (prefixLength < oldPrefixLength) {                 for (j <- (if (take) prefixLength else prefixLength) until oldPrefixLength) {                   println("j: "+j)                   if (take && j < buf.length && (prdVals isDefinedAt j)) {                     ds += Remove(prefixLength, prdVals(j)._1)                     buf.remove(prefixLength)                   } else if (!take && j - prefixLength <= buf.length && (prdVals isDefinedAt j + 1)) {                     ds += Include(j - prefixLength, prdVals(j + 1)._1)                     buf.insert(j - prefixLength, prdVals(j + 1)._1)                   }                 }               }
            case Update(i, o, e) =>               applyDelta(Remove(i, o))               applyDelta(Include(i, o))           }         }         println("prev.parent: "+prev.parent)         println("parentUpdated: "+parentUpdated)         println("take: "+take)         SeqDelta flatten List(parentUpdated.fromDelta) foreach { d =>           println("Delta: "+d)           println(">prdVals: "+prdVals)           println(">prefixLength: "+prefixLength)           println(">buf: "+buf)           println(">ds: "+ds)           applyDelta(d)           println("<prdVals: "+prdVals)           println("<prefixLength: "+prefixLength)           println("<buf: "+buf)           println("<ds: "+ds)         }         (Batch(ds: _*), prdVals, buf.toSeq)       }     }   }

Sophie
Joined: 2011-11-10,
User offline. Last seen 42 years 45 weeks ago.
Re: [Lift] Reactive updates coming soon ??"?
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 15.0px; font: 14.0px Helvetica} p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 15.0px; font: 14.0px Helvetica; min-height: 17.0px} p.p3 {margin: 0.0px 0.0px 0.0px 12.0px; font: 16.0px Times; color: #161616} p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 17.0px; font: 14.0px Helvetica; min-height: 17.0px} p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 17.0px; font: 14.0px Helvetica} p.p6 {margin: 0.0px 0.0px 0.0px 12.0px; font: 16.0px Times; color: #161616; min-height: 19.0px} span.s1 {direction: ltr; unicode-bidi: embed} span.s2 {text-decoration: underline ; direction: ltr; unicode-bidi: embed}

On 2012-01-12 01:55:17 -0600, Naftoli Gugenheim said:


Can you be specific?


Have not used it yet, but just recall some older writings about memory leaks in early FRP implementations.


I haven't benchmarked anything but in general it doesn't do anything too fancy that I'd expect it to have performance issues. Ultimately it's a bunch of thin wrappers around the imperative way of doing things.

I believe the not-yet-published DeltaSeq (a rewrite of TransformedSeq, used from SeqSignal) may need some tuning though (right now it needs a bug fixed, actually --- that's why it's not published yet).


What do you mean by leaks? If you mean memory leak I copied Ingo's technique of using WeakRefernences and Observing reference-holders, so that shouldn't be a problem.


Otherwise, can you be more specific? Is there a project that you're considering using it for that's performance sensitive --- if you elaborate I may do a better job at ruling out your concern. :)



On Mon, Jan 9, 2012 at 11:23 AM, Sophie <itsme213@hotmail.com> wrote:

This project sounds really good. Could you tell a bit about performance / various leak-like issues that FRP can face?


Thanks!


Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon ??"?

On Thu, Jan 12, 2012 at 8:55 AM, Naftoli Gugenheim wrote:
> What do you mean by leaks? If you mean memory leak I copied Ingo's technique
> of using WeakRefernences and Observing reference-holders, so that shouldn't
> be a problem.

See e.g.

http://c2.com/cgi/wiki?FunctionalReactiveProgramming

about what is called a leak in FRP. I can also remember some academic
paper which had quite some information about leaks and about remedies
but I can't recall which one it was.

In any case, it makes sense to listen to Conal Elliot and try to learn
from his experiences:

e.g. http://conal.net/blog/posts/why-classic-frp-does-not-fit-interactive-beh...

Sophie
Joined: 2011-11-10,
User offline. Last seen 42 years 45 weeks ago.
Re: [Lift] Reactive updates coming soon ??"?

On 2012-01-12 01:55:17 -0600, Naftoli Gugenheim said:

> Can you be specific?

http://conal.net/blog/tag/functional-reactive-programming says quite a
bit about these, including "leaks". Hth.

> I haven't benchmarked anything but in general it doesn't do anything
> too fancy that I'd expect it to have performance issues. Ultimately
> it's a bunch of thin wrappers around the imperative way of doing things.
> I believe the not-yet-published DeltaSeq (a rewrite of TransformedSeq,
> used from SeqSignal) may need some tuning though (right now it needs a
> bug fixed, actually --- that's why it's not published yet).
>
> What do you mean by leaks? If you mean memory leak I copied Ingo's
> technique of using WeakRefernences and Observing reference-holders, so
> that shouldn't be a problem.
>
> Otherwise, can you be more specific? Is there a project that you're
> considering using it for that's performance sensitive --- if you
> elaborate I may do a better job at ruling out your concern. :)
>
>
> On Mon, Jan 9, 2012 at 11:23 AM, Sophie wrote:
> This project sounds really good. Could you tell a bit about performance
> / various leak-like issues that FRP can face?
>
> Thanks!
>
> On 2012-01-08 00:25:25 -0600, Naftoli Gugenheim said:

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon ??"?
I think those articles discuss FRP in a much more pure form than reactive currently implements. I might have more to say about those articles if I understood Haskell. However I'm pretty sure that those issues aren't applicable. Also this router blocks the c2.com link so I couldn't open it.

On Thu, Jan 12, 2012 at 11:49 AM, Sophie <itsme213@hotmail.com> wrote:
On 2012-01-12 01:55:17 -0600, Naftoli Gugenheim said:

Can you be specific?

http://conal.net/blog/tag/functional-reactive-programming says quite a bit about these, including "leaks". Hth.

I haven't benchmarked anything but in general it doesn't do anything too fancy that I'd expect it to have performance issues. Ultimately it's a bunch of thin wrappers around the imperative way of doing things.
I believe the not-yet-published DeltaSeq (a rewrite of TransformedSeq, used from SeqSignal) may need some tuning though (right now it needs a bug fixed, actually --- that's why it's not published yet).

What do you mean by leaks? If you mean memory leak I copied Ingo's technique of using WeakRefernences and Observing reference-holders, so that shouldn't be a problem.

Otherwise, can you be more specific? Is there a project that you're considering using it for that's performance sensitive --- if you elaborate I may do a better job at ruling out your concern. :)


On Mon, Jan 9, 2012 at 11:23 AM, Sophie <itsme213@hotmail.com> wrote:
This project sounds really good. Could you tell a bit about performance / various leak-like issues that FRP can face?

Thanks!

On 2012-01-08 00:25:25 -0600, Naftoli Gugenheim said:



Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה

Another requirement of the algorithm that I don't think I mentioned is that it must generate the deltas relative to the dropWhile of the previous parent.

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה
With G-d's help, I was finally able to fix dropWhile and get the tests to pass. For some reason although they all pass on 2.8.1 and 2.9.1, on 2.9.0-1 there are some failures, so until that's looked into I disabled cross building to 2.9.0-1.
When the build finishes all the updates I mentioned in the OP should be published and available, except for most of #2 Widgets -- only Messages is already in the repository. There should be a demo of it on the site when cloudbees finishes, at http://www.reactive-web.co.cc/widgets/Messages.
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה
TableView/TableEditor are basically done, although they aren't in the repository yet.
Some more updates are in the repository, although they haven't been published yet:
1. Signal.mergeAllThis is very helpful for "aggregate" signals. For instance if you have  List[Signal[A]] or a Signal[Seq[Signal[A]]] and you want a total, or average, etc. Now you can write signal.mergeAll and get a Signal[List[A]] (which of course you can then map to get a sum etc.)
2. EventStream.throttleAs requested in another thread. This often desirable in user interfaces: you want something to happen when the user has stopped typing for lets say one second. Also available on JsEventStream.
3. Nicer syntax for defining listeners// javascript listeneron[Click]{ e: JsExp[JsObj] =>  Return(false)}// server listeneronServer[Click]{ _ => println("Clicked!") }
DomEventSource#propagateJS was renamed to js, so you can write:".link [href]" #> onServer[Click]{_ => ... }.js
I think it looks much nicer than writing ".link [href]" #> (DomEventSource.click ->> { ... }).propagateJS

4. Wrap scala functions as javascript ajax callThe javascript DSL already lets you write javascript functions in scala code. Now you can call scala functions from within the javascript DSL. For instance:
on[KeyUp]{ e: JsExp[JsObj] =>  If(e.keyCode == 40 || e.keyCode == 38) Ajax({ i: Int => /* do something on the server */ })(e.keyCode)}


On Wed, Jan 18, 2012 at 6:00 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
With G-d's help, I was finally able to fix dropWhile and get the tests to pass. For some reason although they all pass on 2.8.1 and 2.9.1, on 2.9.0-1 there are some failures, so until that's looked into I disabled cross building to 2.9.0-1.
When the build finishes all the updates I mentioned in the OP should be published and available, except for most of #2 Widgets -- only Messages is already in the repository. There should be a demo of it on the site when cloudbees finishes, at http://www.reactive-web.co.cc/widgets/Messages.

etorreborre 2
Joined: 2011-02-23,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה
Hi Naftoli,
About the first method name "mergeAll", could it be named "sequence" as an analogy to what Scalaz offers for a list of Applicatives: http://stackoverflow.com/questions/6750609/list-of-options-equivalent-of-sequence-in-scala?
Eric.
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: [Lift] Reactive updates coming soon בע"ה
Thanks for the tip! I just renamed it.

On Tue, Jan 31, 2012 at 10:16 PM, etorreborre <etorreborre@gmail.com> wrote:
Hi Naftoli,
About the first method name "mergeAll", could it be named "sequence" as an analogy to what Scalaz offers for a list of Applicatives: http://stackoverflow.com/questions/6750609/list-of-options-equivalent-of-sequence-in-scala?
Eric.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland