Как получить неявное ownerCtx: rx.Ctx.Owner в scala.rx

Вот базовый пример "Привет, мир!", Использующий scala.rx в версии 0.3.1.

Не компилируется из-за отсутствия implicit ownerCtx: rx.Ctx.Owner, Как мне получить этот экземпляр?

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

Интересно, что в scala REPL это работает!?

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> 

Обновить:

После добавления implicit val ctx = Ctx.Owner.Unsafe код компилируется. Но это не выглядит безопасным...

1 ответ

Кажется, что предоставление неявного значения rx.Ctx.Owner выполняется автоматически на макро-магическом уровне, только когда код запрашивает rx.Ctx.Owner находится внутри блока кода, который будет выполняться только один раз. Это включает object s, val s, lazy val с и т. д.

Этот пример будет компилироваться без проблем, потому что val c = ... будет оценивать только один раз.

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()}
  }
}

Точно так же пример, упомянутый в вопросе, но вставленный в scala REPL.

Такое ограничение связано с проблемой утечек Rx в библиотеке scala.rx. Они присутствуют при создании Rx-переменных более высокого порядка (Rx-переменная, которая содержит другую Rx-переменную). Подробнее о проблеме утечек можно прочитать на сайте проекта sala.rx.

Как средство от утечек - концепция rx.Ctx.Owner был представлен и вудо-макро. Этот отрывок из scala.rx показывает интересные части. Заметка Owner сопутствующий объект и 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]
}

Оказывается, что статические блоки кода оцениваются только один раз и не подвержены утечкам. Вот почему voodoo позволяет компилятору найти неявное. Попробуйте разработать код таким способом.

В случаях, когда код не является статичным, и вы уверены, что ваш код будет оцениваться только один раз (например, скрипты в тесте), общее решение заключается в предоставлении Unsafe неявный экземпляр из Owner сопутствующий объект. Просто импортировать import Ctx.Owner.Unsafe._ затем.

Вот как это делается в BasicTests источников scala.rx:

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._
  ...
Другие вопросы по тегам