1

Here goes basic hello world example using scala.rx in version 0.3.1.

It doesn't compile because of lack of implicit ownerCtx: rx.Ctx.Owner. How do I get this instance?

import rx._
val a = Var(1)
val b = Var(2)
val c = Rx{ a() + b() }

This Rx might leak! Either explicitly mark it unsafe (Rx.unsafe) or ensure an implicit RxCtx is in scope!
[error]     val c = Rx{ a() + b() }
[error]               ^
[error] one error found

The interesting thing is that in scala REPL it works!?

scala> import rx._
val a = Var(1)
val b = Var(2)
val c = Rx{ a() + b() }
import rx._

scala> a: rx.Var[Int] = Var@2c(1)
scala> b: rx.Var[Int] = Var@2f(2)
scala> c: rx.Rx.Dynamic[Int] = Rx@d1(3)
scala> 

Update:

After adding implicit val ctx = Ctx.Owner.Unsafe code compiles. But this doesn't look safe...

pawel.panasewicz
  • 1,831
  • 16
  • 27

1 Answers1

0

It seems that providing implicit value of rx.Ctx.Owner is done auto-macro-magically only when code requesting for rx.Ctx.Owner is inside code block which will run only once. This includes objects, vals, lazy vals, etc.

This example will compile without an issue because val c = ... will evaluate only once.

object Once {
  val a = Var(1)
  val b = Var(2)

  //no need to provide `implicit ownerCtx: rx.Ctx.Owner`
  val c = Rx {
    a() + b() -> Rx {a() - b()}
  }
}

Similarly example mentioned in question but pasted in scala REPL.

Such limitation is because of Rx leaks problem in scala.rx library. They are present when creating higher ordered Rx variables (Rx variable which contains another Rx variable). More about an issue of leaks can be read on sala.rx project site.

As remedy to leaks - concept of rx.Ctx.Owner was introduced and voodo-macro. This exceprt from scala.rx shows interesting parts. Note Owner companion object and implicit def voodoo: Owner:

object Owner extends Generic[Owner]{

  object Unsafe extends Owner(???){
    implicit val Unsafe: Ctx.Owner.Unsafe.type = this
  }
  /**
    * Dark magic. End result is the implicit ctx will be one of
    *  1) The enclosing RxCtx, if it exists
    *  2) RxCtx.Unsafe, if in a "static context"
    *  3) RxCtx.CompileTime, if in a "dynamic context" (other macros will rewrite CompileTime away)
    */
  @compileTimeOnly("@}}>---: A rose by any other name.")
  implicit def voodoo: Owner = macro Factories.automaticOwnerContext[rx.Ctx.Owner]
}

It turns out that static blocks of code evaluate only once and are immune of leaks. This is why voodoo allows compiler to find implicit. Try to design code in such way.

In cases when code is not in static one and you are sure that your code will evaluate only once (scripts in test for example) the common solution is to provide Unsafe implicit instance from Owner companion object. Just import import Ctx.Owner.Unsafe._ then.

Here how it is done in BasicTests of scala.rx sources:

package rx
import util.{Failure, Success}

import utest._
import acyclic.file
object BasicTests extends TestSuite{

  //We dont care about potential Rx leaks in BasicTest
  import Ctx.Owner.Unsafe._
  ...
pawel.panasewicz
  • 1,831
  • 16
  • 27