Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    Felix Dietze
    @fdietze
    @SRGOM I agree with you. A huge audience would justify this kind of diversity. But I believe that we don't have enough library developers to have ~7 different libraries with the same goals.
    Zoo Sky
    @zoosky
    @lihaoyi what's the state of this project?
    Li Haoyi
    @lihaoyi
    it works, feel free to use it
    Fabian Schmitthenner
    @fkz
    @Voltir @lihaoyi hey, may you have a look at this issue #89, please?
    Nick Childers
    @Voltir
    Thanks for the sample @fkz - I can find some time to poke at it at the very least, and try to figure out way it happens
    Fabian Schmitthenner
    @fkz
    @Voltir Have you been able to reproduce it?
    Nick Childers
    @Voltir
    @fkz Yea - I was able to repo it, how very interesting
    Nick Childers
    @Voltir
    it looks to be related to something about mapping Rx'es..
    to be fair, I wrote that before I knew what the functor laws were.. i should probably revisit that code to make sure those things are lawful!
    Fabian Schmitthenner
    @fkz

    I don’t think this has anything to do with map or functor laws, as replacing map with the corresponding Rx construct leads to the same problem:

    import $ivy.`com.lihaoyi::scalarx:0.3.2`, rx._
    object Example {
    val v1 = Var(0)
    val v2 = Rx { v1() }
    val v22 = Rx { v1() }
    val v23 = Rx { v22() }
    val v3 = Rx { v23() }
    def q(implicit trackDependency: Ctx.Data) = {
        if (v1() == 0) v2()
        else {
          val b = v3() != v2()
          if (b)
            103
          else
            17
        }
      }
    val v = Rx { q }
    val list = {
        var result = List.empty[Int]
        val obs = v.trigger { result = result :+ v.now }
        v1() = 1
        v1() = 2
        result
      }
    }
    println(Example.list)

    This prints List(0, 103, 17) instead of List(0, 17), too.

    Felix Dietze
    @fdietze
    Hi, does anyone know which guarantees regarding frp glitches and multiple updates I get in a synchronous setting?
    Roland Reckel
    @rreckel

    Hi,
    I've got a problem with calling Futures out of Rx.
    Just copy and paste the following code into ScalaFiddle:

    import rx._
    import rx.async._
    import scala.concurrent.Future
    import scala.concurrent.Promise
    import scala.concurrent.ExecutionContext.Implicits.global
    
    def request(id: Int): Future[String] = {
      val p = Promise[String]()
      js.timers.setTimeout(500) {
        println(s"Request: $id")
        p.success(id.toString)
      }
      p.future
    }
    
    val id = Var[Int](0)
    val idString = id.flatMap(i => request(i).toRx("...")) 
    
    id() = 10
    println("End...")

    And you fall into a loop, with the following output:

    End...
    Request: 0
    Request: 10
    Request: 10
    Request: 10
    Request: 10
    Request: 10
    Request: 10
    Request: 10
    etc....

    Is this a known behaviour? or is this a bug?
    Is there a workaround for this kind of pattern?

    Thanks for your help.
    Roll

    Nick Childers
    @Voltir

    @rreckel Yea, currently the way flatMap works, it recreates the inner rx whenever the inner rx changes - this interacts poorly with toRx because, as you see in your test, it throws it in an infinite loop.

    Im having a hard time coming up with an alternate definition of toRx that actually works within flatmap and am considering just removing it all together.

    Nick Childers
    @Voltir

    So ive been playing around with alternatives, and this seems to work:

      implicit class FutureCombinators[T](val v: rx.Rx[Future[T]]) extends AnyVal {
        def flatten(initial: T)(implicit ec: ExecutionContext, ctx: Ctx.Owner): Rx[T] = {
          var result = Var(initial)
          v.foreach { x => x.foreach(result.update) }
          result
        }
      }

    And its used this way:
    id.map { x => request(x) }.flatten("...")

    @rreckel would something like that work? @fdietze what do you think?
    Nick Childers
    @Voltir
    Bleh, that actually doesnt work if you end up having the flattened future inside of a flatMap
    Roland Reckel
    @rreckel

    I tried your flatten function:

    import rx._
    import rx.async._
    import scala.concurrent.Future
    import scala.concurrent.Promise
    import scala.concurrent.ExecutionContext
    
    implicit class FutureCombinators[T](val v: rx.Rx[Future[T]]) extends AnyVal {
      def flatten(initial: T)(implicit ec: ExecutionContext, ctx: Ctx.Owner): Rx[T] = {
        val result = Var(initial)
        v.foreach { x => x.foreach(result.update) }
        result
      }
    }
    
    def request(id: Int): Future[String] = {
      val p = Promise[String]()
      js.timers.setTimeout(500) {
        println(s"Request: $id")
        p.success(id.toString)
      }
      p.future
    }
    
    import scala.concurrent.ExecutionContext.Implicits.global
    
    val id = Var[Int](0)
    val idString = id.map(i => request(i)) .flatten("...")
    
    val result = for {
      i <- id
      is <- idString
    } yield s"$i -> $is"
    
    result.foreach(println(_))
    
    id() = 10
    id() = 20
    println("End...")

    This code generates the following result in ScalaFiddle:

    0 -> ...
    10 -> ...
    20 -> ...
    End...
    Request: 0
    20 -> 0
    Request: 10
    20 -> 10
    Request: 20
    20 -> 20

    This is not exactly what I expected..... but still better than an infinite loop ;-)
    What do you think?

    Nick Childers
    @Voltir
    Interesting - I tried a similar test but had the map/flatten in the for loop and that created an infinite loop
    Maybe this and a section in the readme explaining this is sufficient?
    Felix Dietze
    @fdietze
    Are you trying with a 0.3 version or with latest master? To me it looks like the flatMap leak, which we fixed recently.
    Roland Reckel
    @rreckel
    I am using version 0.3.2
    I integrated the flatten function in my project now, and I don't have any loops anymore.
    Does an "official" latest version of scala.rx exist? I often try some snippets of code in ScalaFiddle but there the only version is 0.3.2
    Anyway I can test the code with the latest master and keep you informed.
    Roland Reckel
    @rreckel

    Hi again,
    I am sorry to report that the latest master does not resolve the loop in flatMap with toRx.
    The following script can be executed in ammonite:

    import $ivy.`com.lihaoyi::scalarx:0.4.0-SNAPSHOT`, rx._, rx.async._
    import Ctx.Owner.Unsafe._
    
    import scala.concurrent._
    import scala.concurrent.duration._
    import scala.concurrent.ExecutionContext.Implicits.global
    
    object FlatMap {
    
      def test() = {
        def request(id: Int): Future[String] = Future {
          Thread.sleep(500)
          println(s"Request: $id")
          id.toString
        }
    
        val id = Var[Int](0)
        val idString = id.flatMap(i => request(i).toRx("..."))
    
        id() = 10
        id() = 20
        println("End...")
        Await.result(Future{Thread.sleep(3000); println("Waited long enough")}, 30 seconds)
      }
    }
    
    FlatMap.test()

    and produces the following output:

    End...
    Request: 0
    Request: 10
    Request: 20
    Request: 20
    Request: 20
    Request: 20
    Request: 20
    Waited long enough
    Request: 20
    Felix Dietze
    @fdietze
    @rreckel Could you please open a PR with a failing test case?
    Nick Childers
    @Voltir
    I've been trying to tackle this problem - it's not the same issue as the flatmap problem. It also seems really hard to solve in a satisfying way.
    Making futures compose nicely in Rx contexts is hard because the re-evaluation step recreates the future, creating the cycle of infinite loop
    Roland Reckel
    @rreckel
    Call me crazy, but would it be possible to create a "toRx" function for Monix Task? Would that be easier perhaps, as those are lazy evaluated?
    I guess I will have to look at the scala.rx code a bit.
    Nick Childers
    @Voltir
    Sure - I think that's possible
    Perhaps something akin to how sttp does its effect encoding https://github.com/softwaremill/sttp
    I wouldn't want to add a dependency to monix in the core repo though
    Roland Reckel
    @rreckel
    You are right.... adding a dependency is not good.
    Nick Childers
    @Voltir
    What gives me hope is that toRx does appear to work if the definition is outside of the flatmap - I think if I can make the flatmap macro generate the equivalent of that, we might finally have a decent solution
    Minghao Liu
    @molikto
    @  val a = Var(1)  
    
    a: Var[Int] = Var@70(1)
    
    @  
    
    @ val b = Var(2)  
    b: Var[Int] = Var@6b(2)
    
    @  val c = Rx { println("c triggered "+ ( a() + b()))}  
    c triggered 3
    c: Rx.Dynamic[Unit] = Rx@2e(())
    
    
    @  
    
    @ a.kill() 
    
    
    @ b() = 3 
    c triggered 4
    
    
    @ a() = 5 
    c triggered 8
    so seems kill() doesn't work on Var?
    Nick Childers
    @Voltir
    @molikto ah, thats interesting - what happens if you dont do the b() = 3 step?
    kill on a Var just clears the downstream listeners - Vars dont really have an owner. What I think happens is when you trigger a re-evaluation of c via b() = 3, the dependency c has on a is just recreated. I guess "kill" is kind of a misleading name for what actually happens on a Var.
    Peng Li
    @freewind
    Hi guys, I have some questions when testing scala.rx code, and post a question here: https://stackoverflow.com/questions/49073908/how-to-test-vars-of-scala-rx-with-scalatest
    Thanks for helping
    Nick Childers
    @Voltir
    @freewind i took a stab at answering it, let me know if you want any clarification
    Peng Li
    @freewind
    @Voltir Thanks for your answer! It gives me some idea and I changed my test using scalatest's AsyncTestSuite and the code is:
    test("watchOrders") {
      var result = List.empty[Order]
      val stream = client.watchOrders()
      val promise = Promise[Assertion]()
      stream.fold(List.empty[Order]) {
        case (acc, Some(elem)) =>
          val result = elem :: acc
          println("result size: " + result.size)
          if (result.size == 4) promise.success({
            assert(validateTheOrders(result))
          })
          result
      }
      promise.future
    }
    The good new is if the validateTheOrders(result) is passed, the test will pass and terminate as expected
    But if the assertion is false, it will never stop, and the result size printed in console will be:
    result size: 1
    result size: 2
    result size: 3
    result size: 4
    result size: 4
    result size: 4
    result size: 4
    ...
    Peng Li
    @freewind

    @Voltir I just make it work inspired with the eventually method from haoyi's utest. Now I'm still using scalatest's SyncTestSuit, and the code is like:

      private def waitUntil[T](stream: Rx[T])(cond: T => Boolean, maxWait: Duration): T = {
        val promise = Promise[T]()
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() => {
          val list = stream.now
          if (cond(list)) {
            promise.success(list)
          }
        }, 0L, 100L, TimeUnit.MILLISECONDS)
        Await.result(promise.future, maxWait)
      }
    
      test("watchOrders") {
        val stream = client.watchOrders()
        val listRx: Rx[List[Order]] = stream.fold(List.empty[Order]) {
          case (acc, Some(elem)) => elem :: acc
          case (acc, _) => acc
        }
        val list = waitUntil(listRx)(_.size >= 4, 10.seconds)
        assert(validateTheOrders(list))
      }

    It's working great now

    Nick Childers
    @Voltir
    @/all scala.rx 0.4.0 was published
    scalway
    @scalway
    @Voltir where? I cannot find sources.
    Oh... there is still old documentation & there is no github release :).