Как получить неявное 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._
...